diff options
author | Artem Zuikov <chertus@gmail.com> | 2022-02-10 16:46:28 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:46:28 +0300 |
commit | 889e4ac5f8ac5a463b06b9b64de835d736c06980 (patch) | |
tree | 1a2c5ffcf89eb53ecd79dbc9bc0a195c27404d0c /contrib/libs | |
parent | d23c9e2333524245de2f721e04136f51c31411ef (diff) | |
download | ydb-889e4ac5f8ac5a463b06b9b64de835d736c06980.tar.gz |
Restoring authorship annotation for Artem Zuikov <chertus@gmail.com>. Commit 2 of 2.
Diffstat (limited to 'contrib/libs')
83 files changed, 18246 insertions, 18246 deletions
diff --git a/contrib/libs/apache/arrow/.yandex_meta/devtools.licenses.report b/contrib/libs/apache/arrow/.yandex_meta/devtools.licenses.report index f3f9e90568..1f1bbe7849 100644 --- a/contrib/libs/apache/arrow/.yandex_meta/devtools.licenses.report +++ b/contrib/libs/apache/arrow/.yandex_meta/devtools.licenses.report @@ -1308,25 +1308,25 @@ BELONGS ya.make cpp/src/arrow/compute/util_internal.h [1:16] cpp/src/arrow/config.cc [1:16] cpp/src/arrow/config.h [1:16] - cpp/src/arrow/csv/api.h [1:16] - cpp/src/arrow/csv/chunker.cc [1:16] - cpp/src/arrow/csv/chunker.h [1:16] - cpp/src/arrow/csv/column_builder.cc [1:16] - cpp/src/arrow/csv/column_builder.h [1:16] - cpp/src/arrow/csv/column_decoder.cc [1:16] - cpp/src/arrow/csv/column_decoder.h [1:16] - cpp/src/arrow/csv/converter.cc [1:16] - cpp/src/arrow/csv/converter.h [1:16] - cpp/src/arrow/csv/inference_internal.h [1:16] - cpp/src/arrow/csv/options.cc [1:16] - cpp/src/arrow/csv/options.h [1:16] - cpp/src/arrow/csv/parser.cc [1:16] - cpp/src/arrow/csv/parser.h [1:16] - cpp/src/arrow/csv/reader.cc [1:16] - cpp/src/arrow/csv/reader.h [1:16] - cpp/src/arrow/csv/type_fwd.h [1:16] - cpp/src/arrow/csv/writer.cc [1:16] - cpp/src/arrow/csv/writer.h [1:16] + cpp/src/arrow/csv/api.h [1:16] + cpp/src/arrow/csv/chunker.cc [1:16] + cpp/src/arrow/csv/chunker.h [1:16] + cpp/src/arrow/csv/column_builder.cc [1:16] + cpp/src/arrow/csv/column_builder.h [1:16] + cpp/src/arrow/csv/column_decoder.cc [1:16] + cpp/src/arrow/csv/column_decoder.h [1:16] + cpp/src/arrow/csv/converter.cc [1:16] + cpp/src/arrow/csv/converter.h [1:16] + cpp/src/arrow/csv/inference_internal.h [1:16] + cpp/src/arrow/csv/options.cc [1:16] + cpp/src/arrow/csv/options.h [1:16] + cpp/src/arrow/csv/parser.cc [1:16] + cpp/src/arrow/csv/parser.h [1:16] + cpp/src/arrow/csv/reader.cc [1:16] + cpp/src/arrow/csv/reader.h [1:16] + cpp/src/arrow/csv/type_fwd.h [1:16] + cpp/src/arrow/csv/writer.cc [1:16] + cpp/src/arrow/csv/writer.h [1:16] cpp/src/arrow/datum.cc [1:16] cpp/src/arrow/datum.h [1:16] cpp/src/arrow/device.cc [1:16] diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/api.h b/contrib/libs/apache/arrow/cpp/src/arrow/api.h index 1ac5b20893..8958eaf1c9 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/api.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/api.h @@ -1,44 +1,44 @@ -// 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. - -// Coarse public API while the library is in development - -#pragma once - -#include "arrow/array.h" // IYWU pragma: export -#include "arrow/array/concatenate.h" // IYWU pragma: export -#include "arrow/buffer.h" // IYWU pragma: export -#include "arrow/builder.h" // IYWU pragma: export -#include "arrow/chunked_array.h" // IYWU pragma: export -#include "arrow/compare.h" // IYWU pragma: export -#include "arrow/config.h" // IYWU pragma: export -#include "arrow/datum.h" // IYWU pragma: export -#include "arrow/extension_type.h" // IYWU pragma: export -#include "arrow/memory_pool.h" // IYWU pragma: export -#include "arrow/pretty_print.h" // IYWU pragma: export -#include "arrow/record_batch.h" // IYWU pragma: export -#include "arrow/result.h" // IYWU pragma: export -#include "arrow/status.h" // IYWU pragma: export -#include "arrow/table.h" // IYWU pragma: export -#include "arrow/table_builder.h" // IYWU pragma: export -#include "arrow/tensor.h" // IYWU pragma: export -#include "arrow/type.h" // IYWU pragma: export -#include "arrow/util/key_value_metadata.h" // IWYU pragma: export -#include "arrow/visitor.h" // IYWU pragma: export - -/// \brief Top-level namespace for Apache Arrow C++ API -namespace arrow {} +// 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. + +// Coarse public API while the library is in development + +#pragma once + +#include "arrow/array.h" // IYWU pragma: export +#include "arrow/array/concatenate.h" // IYWU pragma: export +#include "arrow/buffer.h" // IYWU pragma: export +#include "arrow/builder.h" // IYWU pragma: export +#include "arrow/chunked_array.h" // IYWU pragma: export +#include "arrow/compare.h" // IYWU pragma: export +#include "arrow/config.h" // IYWU pragma: export +#include "arrow/datum.h" // IYWU pragma: export +#include "arrow/extension_type.h" // IYWU pragma: export +#include "arrow/memory_pool.h" // IYWU pragma: export +#include "arrow/pretty_print.h" // IYWU pragma: export +#include "arrow/record_batch.h" // IYWU pragma: export +#include "arrow/result.h" // IYWU pragma: export +#include "arrow/status.h" // IYWU pragma: export +#include "arrow/table.h" // IYWU pragma: export +#include "arrow/table_builder.h" // IYWU pragma: export +#include "arrow/tensor.h" // IYWU pragma: export +#include "arrow/type.h" // IYWU pragma: export +#include "arrow/util/key_value_metadata.h" // IWYU pragma: export +#include "arrow/visitor.h" // IYWU pragma: export + +/// \brief Top-level namespace for Apache Arrow C++ API +namespace arrow {} diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_binary.h b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_binary.h index c895240e23..62edc69fb8 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_binary.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_binary.h @@ -53,7 +53,7 @@ class BaseBinaryBuilder : public ArrayBuilder { explicit BaseBinaryBuilder(MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool), offsets_builder_(pool), value_data_builder_(pool) {} - BaseBinaryBuilder(const std::shared_ptr<DataType>& /*type*/, MemoryPool* pool) + BaseBinaryBuilder(const std::shared_ptr<DataType>& /*type*/, MemoryPool* pool) : BaseBinaryBuilder(pool) {} Status Append(const uint8_t* value, offset_type length) { diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_dict.h b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_dict.h index bb43658868..eb96482dbf 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_dict.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_dict.h @@ -421,11 +421,11 @@ class DictionaryBuilderBase<BuilderType, NullType> : public ArrayBuilder { DictionaryBuilderBase( enable_if_t<std::is_base_of<AdaptiveIntBuilderBase, B>::value, uint8_t> start_int_size, - const std::shared_ptr<DataType>& /*value_type*/, + const std::shared_ptr<DataType>& /*value_type*/, MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool), indices_builder_(start_int_size, pool) {} - explicit DictionaryBuilderBase(const std::shared_ptr<DataType>& /*value_type*/, + explicit DictionaryBuilderBase(const std::shared_ptr<DataType>& /*value_type*/, MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool), indices_builder_(pool) {} @@ -439,7 +439,7 @@ class DictionaryBuilderBase<BuilderType, NullType> : public ArrayBuilder { explicit DictionaryBuilderBase(MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool), indices_builder_(pool) {} - explicit DictionaryBuilderBase(const std::shared_ptr<Array>& /*dictionary*/, + explicit DictionaryBuilderBase(const std::shared_ptr<Array>& /*dictionary*/, MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool), indices_builder_(pool) {} diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_primitive.h b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_primitive.h index 9bd7a52c34..80cfc4061b 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_primitive.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/array/builder_primitive.h @@ -32,7 +32,7 @@ namespace arrow { class ARROW_EXPORT NullBuilder : public ArrayBuilder { public: explicit NullBuilder(MemoryPool* pool = default_memory_pool()) : ArrayBuilder(pool) {} - explicit NullBuilder(const std::shared_ptr<DataType>& /*type*/, + explicit NullBuilder(const std::shared_ptr<DataType>& /*type*/, MemoryPool* pool = default_memory_pool()) : NullBuilder(pool) {} diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api.h index 13f1ea762a..a890cd362f 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api.h @@ -1,35 +1,35 @@ -// 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. - -// NOTE: API is EXPERIMENTAL and will change without going through a -// deprecation cycle - -#pragma once - -/// \defgroup compute-concrete-options Concrete option classes for compute functions -/// @{ -/// @} - -#include "arrow/compute/api_aggregate.h" // IWYU pragma: export -#include "arrow/compute/api_scalar.h" // IWYU pragma: export -#include "arrow/compute/api_vector.h" // IWYU pragma: export -#include "arrow/compute/cast.h" // IWYU pragma: export -#include "arrow/compute/exec.h" // IWYU pragma: export -#include "arrow/compute/function.h" // IWYU pragma: export -#include "arrow/compute/kernel.h" // IWYU pragma: export -#include "arrow/compute/registry.h" // IWYU pragma: export -#include "arrow/datum.h" // IWYU pragma: export +// 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. + +// NOTE: API is EXPERIMENTAL and will change without going through a +// deprecation cycle + +#pragma once + +/// \defgroup compute-concrete-options Concrete option classes for compute functions +/// @{ +/// @} + +#include "arrow/compute/api_aggregate.h" // IWYU pragma: export +#include "arrow/compute/api_scalar.h" // IWYU pragma: export +#include "arrow/compute/api_vector.h" // IWYU pragma: export +#include "arrow/compute/cast.h" // IWYU pragma: export +#include "arrow/compute/exec.h" // IWYU pragma: export +#include "arrow/compute/function.h" // IWYU pragma: export +#include "arrow/compute/kernel.h" // IWYU pragma: export +#include "arrow/compute/registry.h" // IWYU pragma: export +#include "arrow/datum.h" // IWYU pragma: export diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.cc index 6e9d9de0c5..1b00c366bf 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.cc @@ -1,30 +1,30 @@ -// 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. - -#include "arrow/compute/api_aggregate.h" - -#include "arrow/compute/exec.h" +// 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. + +#include "arrow/compute/api_aggregate.h" + +#include "arrow/compute/exec.h" #include "arrow/compute/function_internal.h" #include "arrow/compute/registry.h" #include "arrow/compute/util_internal.h" #include "arrow/util/checked_cast.h" #include "arrow/util/logging.h" - -namespace arrow { + +namespace arrow { namespace internal { template <> @@ -52,9 +52,9 @@ struct EnumTraits<compute::QuantileOptions::Interpolation> }; } // namespace internal -namespace compute { - -// ---------------------------------------------------------------------- +namespace compute { + +// ---------------------------------------------------------------------- // Function options using ::arrow::internal::checked_cast; @@ -133,33 +133,33 @@ void RegisterAggregateOptions(FunctionRegistry* registry) { } // namespace internal // ---------------------------------------------------------------------- -// Scalar aggregates - +// Scalar aggregates + Result<Datum> Count(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { - return CallFunction("count", {value}, &options, ctx); -} - + return CallFunction("count", {value}, &options, ctx); +} + Result<Datum> Mean(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { return CallFunction("mean", {value}, &options, ctx); -} - +} + Result<Datum> Sum(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { return CallFunction("sum", {value}, &options, ctx); -} - +} + Result<Datum> MinMax(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { - return CallFunction("min_max", {value}, &options, ctx); -} - + return CallFunction("min_max", {value}, &options, ctx); +} + Result<Datum> Any(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { return CallFunction("any", {value}, &options, ctx); -} - +} + Result<Datum> All(const Datum& value, const ScalarAggregateOptions& options, ExecContext* ctx) { return CallFunction("all", {value}, &options, ctx); @@ -169,16 +169,16 @@ Result<Datum> Mode(const Datum& value, const ModeOptions& options, ExecContext* return CallFunction("mode", {value}, &options, ctx); } -Result<Datum> Stddev(const Datum& value, const VarianceOptions& options, - ExecContext* ctx) { - return CallFunction("stddev", {value}, &options, ctx); -} - -Result<Datum> Variance(const Datum& value, const VarianceOptions& options, - ExecContext* ctx) { - return CallFunction("variance", {value}, &options, ctx); -} - +Result<Datum> Stddev(const Datum& value, const VarianceOptions& options, + ExecContext* ctx) { + return CallFunction("stddev", {value}, &options, ctx); +} + +Result<Datum> Variance(const Datum& value, const VarianceOptions& options, + ExecContext* ctx) { + return CallFunction("variance", {value}, &options, ctx); +} + Result<Datum> Quantile(const Datum& value, const QuantileOptions& options, ExecContext* ctx) { return CallFunction("quantile", {value}, &options, ctx); @@ -193,5 +193,5 @@ Result<Datum> Index(const Datum& value, const IndexOptions& options, ExecContext return CallFunction("index", {value}, &options, ctx); } -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.h index 99ea33f7bf..7a6c44bd92 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_aggregate.h @@ -1,58 +1,58 @@ -// 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. - -// Eager evaluation convenience APIs for invoking common functions, including -// necessary memory allocations - -#pragma once - -#include "arrow/compute/function.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { - -class Array; - -namespace compute { - -class ExecContext; - -// ---------------------------------------------------------------------- -// Aggregate functions - -/// \addtogroup compute-concrete-options -/// @{ - +// 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. + +// Eager evaluation convenience APIs for invoking common functions, including +// necessary memory allocations + +#pragma once + +#include "arrow/compute/function.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { + +class Array; + +namespace compute { + +class ExecContext; + +// ---------------------------------------------------------------------- +// Aggregate functions + +/// \addtogroup compute-concrete-options +/// @{ + /// \brief Control general scalar aggregate kernel behavior -/// +/// /// By default, null values are ignored class ARROW_EXPORT ScalarAggregateOptions : public FunctionOptions { public: explicit ScalarAggregateOptions(bool skip_nulls = true, uint32_t min_count = 1); constexpr static char const kTypeName[] = "ScalarAggregateOptions"; static ScalarAggregateOptions Defaults() { return ScalarAggregateOptions{}; } - + bool skip_nulls; uint32_t min_count; }; - + /// \brief Control Mode kernel behavior /// /// Returns top-n common values and counts. @@ -62,12 +62,12 @@ class ARROW_EXPORT ModeOptions : public FunctionOptions { explicit ModeOptions(int64_t n = 1); constexpr static char const kTypeName[] = "ModeOptions"; static ModeOptions Defaults() { return ModeOptions{}; } - + int64_t n = 1; -}; - +}; + /// \brief Control Delta Degrees of Freedom (ddof) of Variance and Stddev kernel -/// +/// /// The divisor used in calculations is N - ddof, where N is the number of elements. /// By default, ddof is zero, and population variance or stddev is returned. class ARROW_EXPORT VarianceOptions : public FunctionOptions { @@ -91,23 +91,23 @@ class ARROW_EXPORT QuantileOptions : public FunctionOptions { HIGHER, NEAREST, MIDPOINT, - }; - + }; + explicit QuantileOptions(double q = 0.5, enum Interpolation interpolation = LINEAR); - + explicit QuantileOptions(std::vector<double> q, enum Interpolation interpolation = LINEAR); - + constexpr static char const kTypeName[] = "QuantileOptions"; static QuantileOptions Defaults() { return QuantileOptions{}; } /// quantile must be between 0 and 1 inclusive std::vector<double> q; enum Interpolation interpolation; -}; - +}; + /// \brief Control TDigest approximate quantile kernel behavior -/// +/// /// By default, returns the median value. class ARROW_EXPORT TDigestOptions : public FunctionOptions { public: @@ -117,7 +117,7 @@ class ARROW_EXPORT TDigestOptions : public FunctionOptions { uint32_t buffer_size = 500); constexpr static char const kTypeName[] = "TDigestOptions"; static TDigestOptions Defaults() { return TDigestOptions{}; } - + /// quantile must be between 0 and 1 inclusive std::vector<double> q; /// compression parameter, default 100 @@ -125,7 +125,7 @@ class ARROW_EXPORT TDigestOptions : public FunctionOptions { /// input buffer size, default 500 uint32_t buffer_size; }; - + /// \brief Control Index kernel behavior class ARROW_EXPORT IndexOptions : public FunctionOptions { public: @@ -135,73 +135,73 @@ class ARROW_EXPORT IndexOptions : public FunctionOptions { constexpr static char const kTypeName[] = "IndexOptions"; std::shared_ptr<Scalar> value; -}; - -/// @} - -/// \brief Count non-null (or null) values in an array. -/// +}; + +/// @} + +/// \brief Count non-null (or null) values in an array. +/// /// \param[in] options counting options, see ScalarAggregateOptions for more information -/// \param[in] datum to count -/// \param[in] ctx the function execution context, optional -/// \return out resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] datum to count +/// \param[in] ctx the function execution context, optional +/// \return out resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> Count( const Datum& datum, const ScalarAggregateOptions& options = ScalarAggregateOptions::Defaults(), ExecContext* ctx = NULLPTR); - -/// \brief Compute the mean of a numeric array. -/// -/// \param[in] value datum to compute the mean, expecting Array + +/// \brief Compute the mean of a numeric array. +/// +/// \param[in] value datum to compute the mean, expecting Array /// \param[in] options see ScalarAggregateOptions for more information -/// \param[in] ctx the function execution context, optional -/// \return datum of the computed mean as a DoubleScalar -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return datum of the computed mean as a DoubleScalar +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> Mean( const Datum& value, const ScalarAggregateOptions& options = ScalarAggregateOptions::Defaults(), ExecContext* ctx = NULLPTR); - -/// \brief Sum values of a numeric array. -/// -/// \param[in] value datum to sum, expecting Array or ChunkedArray + +/// \brief Sum values of a numeric array. +/// +/// \param[in] value datum to sum, expecting Array or ChunkedArray /// \param[in] options see ScalarAggregateOptions for more information -/// \param[in] ctx the function execution context, optional -/// \return datum of the computed sum as a Scalar -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return datum of the computed sum as a Scalar +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> Sum( const Datum& value, const ScalarAggregateOptions& options = ScalarAggregateOptions::Defaults(), ExecContext* ctx = NULLPTR); - -/// \brief Calculate the min / max of a numeric array -/// -/// This function returns both the min and max as a struct scalar, with type -/// struct<min: T, max: T>, where T is the input type -/// -/// \param[in] value input datum, expecting Array or ChunkedArray + +/// \brief Calculate the min / max of a numeric array +/// +/// This function returns both the min and max as a struct scalar, with type +/// struct<min: T, max: T>, where T is the input type +/// +/// \param[in] value input datum, expecting Array or ChunkedArray /// \param[in] options see ScalarAggregateOptions for more information -/// \param[in] ctx the function execution context, optional -/// \return resulting datum as a struct<min: T, max: T> scalar -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return resulting datum as a struct<min: T, max: T> scalar +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> MinMax( const Datum& value, const ScalarAggregateOptions& options = ScalarAggregateOptions::Defaults(), ExecContext* ctx = NULLPTR); - + /// \brief Test whether any element in a boolean array evaluates to true. /// /// This function returns true if any of the elements in the array evaluates @@ -244,53 +244,53 @@ Result<Datum> All( const ScalarAggregateOptions& options = ScalarAggregateOptions::Defaults(), ExecContext* ctx = NULLPTR); -/// \brief Calculate the modal (most common) value of a numeric array -/// +/// \brief Calculate the modal (most common) value of a numeric array +/// /// This function returns top-n most common values and number of times they occur as /// an array of `struct<mode: T, count: int64>`, where T is the input type. /// Values with larger counts are returned before smaller ones. /// If there are more than one values with same count, smaller value is returned first. -/// -/// \param[in] value input datum, expecting Array or ChunkedArray +/// +/// \param[in] value input datum, expecting Array or ChunkedArray /// \param[in] options see ModeOptions for more information -/// \param[in] ctx the function execution context, optional +/// \param[in] ctx the function execution context, optional /// \return resulting datum as an array of struct<mode: T, count: int64> -/// -/// \since 2.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// +/// \since 2.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> Mode(const Datum& value, const ModeOptions& options = ModeOptions::Defaults(), ExecContext* ctx = NULLPTR); - -/// \brief Calculate the standard deviation of a numeric array -/// -/// \param[in] value input datum, expecting Array or ChunkedArray -/// \param[in] options see VarianceOptions for more information -/// \param[in] ctx the function execution context, optional -/// \return datum of the computed standard deviation as a DoubleScalar -/// -/// \since 2.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Stddev(const Datum& value, - const VarianceOptions& options = VarianceOptions::Defaults(), - ExecContext* ctx = NULLPTR); - -/// \brief Calculate the variance of a numeric array -/// -/// \param[in] value input datum, expecting Array or ChunkedArray -/// \param[in] options see VarianceOptions for more information -/// \param[in] ctx the function execution context, optional -/// \return datum of the computed variance as a DoubleScalar -/// -/// \since 2.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Variance(const Datum& value, - const VarianceOptions& options = VarianceOptions::Defaults(), - ExecContext* ctx = NULLPTR); - + +/// \brief Calculate the standard deviation of a numeric array +/// +/// \param[in] value input datum, expecting Array or ChunkedArray +/// \param[in] options see VarianceOptions for more information +/// \param[in] ctx the function execution context, optional +/// \return datum of the computed standard deviation as a DoubleScalar +/// +/// \since 2.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Stddev(const Datum& value, + const VarianceOptions& options = VarianceOptions::Defaults(), + ExecContext* ctx = NULLPTR); + +/// \brief Calculate the variance of a numeric array +/// +/// \param[in] value input datum, expecting Array or ChunkedArray +/// \param[in] options see VarianceOptions for more information +/// \param[in] ctx the function execution context, optional +/// \return datum of the computed variance as a DoubleScalar +/// +/// \since 2.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Variance(const Datum& value, + const VarianceOptions& options = VarianceOptions::Defaults(), + ExecContext* ctx = NULLPTR); + /// \brief Calculate the quantiles of a numeric array /// /// \param[in] value input datum, expecting Array or ChunkedArray @@ -429,5 +429,5 @@ Result<Datum> GroupBy(const std::vector<Datum>& arguments, const std::vector<Dat ExecContext* ctx = default_exec_context()); } // namespace internal -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.cc index 1d374cb915..1feb4e7eee 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.cc @@ -1,37 +1,37 @@ -// 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. - -#include "arrow/compute/api_scalar.h" - -#include <memory> -#include <sstream> -#include <string> - +// 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. + +#include "arrow/compute/api_scalar.h" + +#include <memory> +#include <sstream> +#include <string> + #include "arrow/array/array_base.h" -#include "arrow/compute/exec.h" +#include "arrow/compute/exec.h" #include "arrow/compute/function_internal.h" #include "arrow/compute/registry.h" #include "arrow/compute/util_internal.h" -#include "arrow/status.h" -#include "arrow/type.h" +#include "arrow/status.h" +#include "arrow/type.h" #include "arrow/util/checked_cast.h" #include "arrow/util/logging.h" - -namespace arrow { + +namespace arrow { namespace internal { template <> @@ -100,8 +100,8 @@ struct EnumTraits<compute::CompareOperator> }; } // namespace internal -namespace compute { - +namespace compute { + // ---------------------------------------------------------------------- // Function options @@ -302,19 +302,19 @@ void RegisterScalarOptions(FunctionRegistry* registry) { } } // namespace internal -#define SCALAR_EAGER_UNARY(NAME, REGISTRY_NAME) \ - Result<Datum> NAME(const Datum& value, ExecContext* ctx) { \ - return CallFunction(REGISTRY_NAME, {value}, ctx); \ - } - -#define SCALAR_EAGER_BINARY(NAME, REGISTRY_NAME) \ - Result<Datum> NAME(const Datum& left, const Datum& right, ExecContext* ctx) { \ - return CallFunction(REGISTRY_NAME, {left, right}, ctx); \ - } - -// ---------------------------------------------------------------------- -// Arithmetic - +#define SCALAR_EAGER_UNARY(NAME, REGISTRY_NAME) \ + Result<Datum> NAME(const Datum& value, ExecContext* ctx) { \ + return CallFunction(REGISTRY_NAME, {value}, ctx); \ + } + +#define SCALAR_EAGER_BINARY(NAME, REGISTRY_NAME) \ + Result<Datum> NAME(const Datum& left, const Datum& right, ExecContext* ctx) { \ + return CallFunction(REGISTRY_NAME, {left, right}, ctx); \ + } + +// ---------------------------------------------------------------------- +// Arithmetic + #define SCALAR_ARITHMETIC_UNARY(NAME, REGISTRY_NAME, REGISTRY_CHECKED_NAME) \ Result<Datum> NAME(const Datum& arg, ArithmeticOptions options, ExecContext* ctx) { \ auto func_name = (options.check_overflow) ? REGISTRY_CHECKED_NAME : REGISTRY_NAME; \ @@ -335,17 +335,17 @@ SCALAR_ARITHMETIC_UNARY(Log10, "log10", "log10_checked") SCALAR_ARITHMETIC_UNARY(Log2, "log2", "log2_checked") SCALAR_ARITHMETIC_UNARY(Log1p, "log1p", "log1p_checked") -#define SCALAR_ARITHMETIC_BINARY(NAME, REGISTRY_NAME, REGISTRY_CHECKED_NAME) \ - Result<Datum> NAME(const Datum& left, const Datum& right, ArithmeticOptions options, \ - ExecContext* ctx) { \ - auto func_name = (options.check_overflow) ? REGISTRY_CHECKED_NAME : REGISTRY_NAME; \ - return CallFunction(func_name, {left, right}, ctx); \ - } - -SCALAR_ARITHMETIC_BINARY(Add, "add", "add_checked") -SCALAR_ARITHMETIC_BINARY(Subtract, "subtract", "subtract_checked") -SCALAR_ARITHMETIC_BINARY(Multiply, "multiply", "multiply_checked") -SCALAR_ARITHMETIC_BINARY(Divide, "divide", "divide_checked") +#define SCALAR_ARITHMETIC_BINARY(NAME, REGISTRY_NAME, REGISTRY_CHECKED_NAME) \ + Result<Datum> NAME(const Datum& left, const Datum& right, ArithmeticOptions options, \ + ExecContext* ctx) { \ + auto func_name = (options.check_overflow) ? REGISTRY_CHECKED_NAME : REGISTRY_NAME; \ + return CallFunction(func_name, {left, right}, ctx); \ + } + +SCALAR_ARITHMETIC_BINARY(Add, "add", "add_checked") +SCALAR_ARITHMETIC_BINARY(Subtract, "subtract", "subtract_checked") +SCALAR_ARITHMETIC_BINARY(Multiply, "multiply", "multiply_checked") +SCALAR_ARITHMETIC_BINARY(Divide, "divide", "divide_checked") SCALAR_ARITHMETIC_BINARY(Power, "power", "power_checked") SCALAR_ARITHMETIC_BINARY(ShiftLeft, "shift_left", "shift_left_checked") SCALAR_ARITHMETIC_BINARY(ShiftRight, "shift_right", "shift_right_checked") @@ -353,7 +353,7 @@ SCALAR_EAGER_BINARY(Atan2, "atan2") SCALAR_EAGER_UNARY(Floor, "floor") SCALAR_EAGER_UNARY(Ceil, "ceil") SCALAR_EAGER_UNARY(Trunc, "trunc") - + Result<Datum> MaxElementWise(const std::vector<Datum>& args, ElementWiseAggregateOptions options, ExecContext* ctx) { return CallFunction("max_element_wise", args, &options, ctx); @@ -364,14 +364,14 @@ Result<Datum> MinElementWise(const std::vector<Datum>& args, return CallFunction("min_element_wise", args, &options, ctx); } -// ---------------------------------------------------------------------- -// Set-related operations - -static Result<Datum> ExecSetLookup(const std::string& func_name, const Datum& data, +// ---------------------------------------------------------------------- +// Set-related operations + +static Result<Datum> ExecSetLookup(const std::string& func_name, const Datum& data, const SetLookupOptions& options, ExecContext* ctx) { if (!options.value_set.is_arraylike()) { - return Status::Invalid("Set lookup value set must be Array or ChunkedArray"); - } + return Status::Invalid("Set lookup value set must be Array or ChunkedArray"); + } std::shared_ptr<DataType> data_type; if (data.type()->id() == Type::DICTIONARY) { data_type = @@ -379,85 +379,85 @@ static Result<Datum> ExecSetLookup(const std::string& func_name, const Datum& da } else { data_type = data.type(); } - + if (options.value_set.length() > 0 && !data_type->Equals(options.value_set.type())) { - std::stringstream ss; + std::stringstream ss; ss << "Array type didn't match type of values set: " << data_type->ToString() << " vs " << options.value_set.type()->ToString(); - return Status::Invalid(ss.str()); - } - return CallFunction(func_name, {data}, &options, ctx); -} - + return Status::Invalid(ss.str()); + } + return CallFunction(func_name, {data}, &options, ctx); +} + Result<Datum> IsIn(const Datum& values, const SetLookupOptions& options, ExecContext* ctx) { return ExecSetLookup("is_in", values, options, ctx); } -Result<Datum> IsIn(const Datum& values, const Datum& value_set, ExecContext* ctx) { +Result<Datum> IsIn(const Datum& values, const Datum& value_set, ExecContext* ctx) { return ExecSetLookup("is_in", values, SetLookupOptions{value_set}, ctx); -} - +} + Result<Datum> IndexIn(const Datum& values, const SetLookupOptions& options, ExecContext* ctx) { return ExecSetLookup("index_in", values, options, ctx); } -Result<Datum> IndexIn(const Datum& values, const Datum& value_set, ExecContext* ctx) { +Result<Datum> IndexIn(const Datum& values, const Datum& value_set, ExecContext* ctx) { return ExecSetLookup("index_in", values, SetLookupOptions{value_set}, ctx); -} - -// ---------------------------------------------------------------------- -// Boolean functions - -SCALAR_EAGER_UNARY(Invert, "invert") -SCALAR_EAGER_BINARY(And, "and") -SCALAR_EAGER_BINARY(KleeneAnd, "and_kleene") -SCALAR_EAGER_BINARY(Or, "or") -SCALAR_EAGER_BINARY(KleeneOr, "or_kleene") -SCALAR_EAGER_BINARY(Xor, "xor") +} + +// ---------------------------------------------------------------------- +// Boolean functions + +SCALAR_EAGER_UNARY(Invert, "invert") +SCALAR_EAGER_BINARY(And, "and") +SCALAR_EAGER_BINARY(KleeneAnd, "and_kleene") +SCALAR_EAGER_BINARY(Or, "or") +SCALAR_EAGER_BINARY(KleeneOr, "or_kleene") +SCALAR_EAGER_BINARY(Xor, "xor") SCALAR_EAGER_BINARY(AndNot, "and_not") SCALAR_EAGER_BINARY(KleeneAndNot, "and_not_kleene") - -// ---------------------------------------------------------------------- - -Result<Datum> Compare(const Datum& left, const Datum& right, CompareOptions options, - ExecContext* ctx) { - std::string func_name; - switch (options.op) { - case CompareOperator::EQUAL: - func_name = "equal"; - break; - case CompareOperator::NOT_EQUAL: - func_name = "not_equal"; - break; - case CompareOperator::GREATER: - func_name = "greater"; - break; - case CompareOperator::GREATER_EQUAL: - func_name = "greater_equal"; - break; - case CompareOperator::LESS: - func_name = "less"; - break; - case CompareOperator::LESS_EQUAL: - func_name = "less_equal"; - break; - } + +// ---------------------------------------------------------------------- + +Result<Datum> Compare(const Datum& left, const Datum& right, CompareOptions options, + ExecContext* ctx) { + std::string func_name; + switch (options.op) { + case CompareOperator::EQUAL: + func_name = "equal"; + break; + case CompareOperator::NOT_EQUAL: + func_name = "not_equal"; + break; + case CompareOperator::GREATER: + func_name = "greater"; + break; + case CompareOperator::GREATER_EQUAL: + func_name = "greater_equal"; + break; + case CompareOperator::LESS: + func_name = "less"; + break; + case CompareOperator::LESS_EQUAL: + func_name = "less_equal"; + break; + } return CallFunction(func_name, {left, right}, nullptr, ctx); -} - -// ---------------------------------------------------------------------- -// Validity functions - -SCALAR_EAGER_UNARY(IsValid, "is_valid") -SCALAR_EAGER_UNARY(IsNull, "is_null") +} + +// ---------------------------------------------------------------------- +// Validity functions + +SCALAR_EAGER_UNARY(IsValid, "is_valid") +SCALAR_EAGER_UNARY(IsNull, "is_null") SCALAR_EAGER_UNARY(IsNan, "is_nan") - -Result<Datum> FillNull(const Datum& values, const Datum& fill_value, ExecContext* ctx) { - return CallFunction("fill_null", {values, fill_value}, ctx); -} - + +Result<Datum> FillNull(const Datum& values, const Datum& fill_value, ExecContext* ctx) { + return CallFunction("fill_null", {values, fill_value}, ctx); +} + Result<Datum> IfElse(const Datum& cond, const Datum& if_true, const Datum& if_false, ExecContext* ctx) { return CallFunction("if_else", {cond, if_true, if_false}, ctx); @@ -494,5 +494,5 @@ Result<Datum> DayOfWeek(const Datum& arg, DayOfWeekOptions options, ExecContext* return CallFunction("day_of_week", {arg}, &options, ctx); } -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.h index edad35f53d..e07e41569a 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_scalar.h @@ -1,55 +1,55 @@ -// 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. - -// Eager evaluation convenience APIs for invoking common functions, including -// necessary memory allocations - -#pragma once - -#include <string> -#include <utility> - -#include "arrow/compute/exec.h" // IWYU pragma: keep -#include "arrow/compute/function.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace compute { - -/// \addtogroup compute-concrete-options -/// -/// @{ - +// 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. + +// Eager evaluation convenience APIs for invoking common functions, including +// necessary memory allocations + +#pragma once + +#include <string> +#include <utility> + +#include "arrow/compute/exec.h" // IWYU pragma: keep +#include "arrow/compute/function.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace compute { + +/// \addtogroup compute-concrete-options +/// +/// @{ + class ARROW_EXPORT ArithmeticOptions : public FunctionOptions { public: explicit ArithmeticOptions(bool check_overflow = false); constexpr static char const kTypeName[] = "ArithmeticOptions"; - bool check_overflow; -}; - + bool check_overflow; +}; + class ARROW_EXPORT ElementWiseAggregateOptions : public FunctionOptions { public: explicit ElementWiseAggregateOptions(bool skip_nulls = true); constexpr static char const kTypeName[] = "ElementWiseAggregateOptions"; static ElementWiseAggregateOptions Defaults() { return ElementWiseAggregateOptions{}; } - + bool skip_nulls; }; @@ -80,11 +80,11 @@ class ARROW_EXPORT MatchSubstringOptions : public FunctionOptions { constexpr static char const kTypeName[] = "MatchSubstringOptions"; /// The exact substring (or regex, depending on kernel) to look for inside input values. - std::string pattern; + std::string pattern; /// Whether to perform a case-insensitive match. bool ignore_case = false; -}; - +}; + class ARROW_EXPORT SplitOptions : public FunctionOptions { public: explicit SplitOptions(int64_t max_splits = -1, bool reverse = false); @@ -150,34 +150,34 @@ class ARROW_EXPORT ExtractRegexOptions : public FunctionOptions { std::string pattern; }; -/// Options for IsIn and IndexIn functions +/// Options for IsIn and IndexIn functions class ARROW_EXPORT SetLookupOptions : public FunctionOptions { public: explicit SetLookupOptions(Datum value_set, bool skip_nulls = false); SetLookupOptions(); constexpr static char const kTypeName[] = "SetLookupOptions"; - - /// The set of values to look up input values into. - Datum value_set; - /// Whether nulls in `value_set` count for lookup. - /// - /// If true, any null in `value_set` is ignored and nulls in the input - /// produce null (IndexIn) or false (IsIn) values in the output. - /// If false, any null in `value_set` is successfully matched in - /// the input. - bool skip_nulls; -}; - + + /// The set of values to look up input values into. + Datum value_set; + /// Whether nulls in `value_set` count for lookup. + /// + /// If true, any null in `value_set` is ignored and nulls in the input + /// produce null (IndexIn) or false (IsIn) values in the output. + /// If false, any null in `value_set` is successfully matched in + /// the input. + bool skip_nulls; +}; + class ARROW_EXPORT StrptimeOptions : public FunctionOptions { public: explicit StrptimeOptions(std::string format, TimeUnit::type unit); StrptimeOptions(); constexpr static char const kTypeName[] = "StrptimeOptions"; - - std::string format; - TimeUnit::type unit; -}; - + + std::string format; + TimeUnit::type unit; +}; + class ARROW_EXPORT PadOptions : public FunctionOptions { public: explicit PadOptions(int64_t width, std::string padding = " "); @@ -209,21 +209,21 @@ class ARROW_EXPORT SliceOptions : public FunctionOptions { int64_t start, stop, step; }; -enum CompareOperator : int8_t { - EQUAL, - NOT_EQUAL, - GREATER, - GREATER_EQUAL, - LESS, - LESS_EQUAL, -}; - +enum CompareOperator : int8_t { + EQUAL, + NOT_EQUAL, + GREATER, + GREATER_EQUAL, + LESS, + LESS_EQUAL, +}; + struct ARROW_EXPORT CompareOptions { - explicit CompareOptions(CompareOperator op) : op(op) {} + explicit CompareOptions(CompareOperator op) : op(op) {} CompareOptions() : CompareOptions(CompareOperator::EQUAL) {} - enum CompareOperator op; -}; - + enum CompareOperator op; +}; + class ARROW_EXPORT MakeStructOptions : public FunctionOptions { public: MakeStructOptions(std::vector<std::string> n, std::vector<bool> r, @@ -254,8 +254,8 @@ struct ARROW_EXPORT DayOfWeekOptions : public FunctionOptions { uint32_t week_start; }; -/// @} - +/// @} + /// \brief Get the absolute value of a value. /// /// If argument is null the result will be null. @@ -269,59 +269,59 @@ Result<Datum> AbsoluteValue(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(), ExecContext* ctx = NULLPTR); -/// \brief Add two values together. Array values must be the same length. If -/// either addend is null the result will be null. -/// -/// \param[in] left the first addend -/// \param[in] right the second addend -/// \param[in] options arithmetic options (overflow handling), optional -/// \param[in] ctx the function execution context, optional -/// \return the elementwise sum -ARROW_EXPORT -Result<Datum> Add(const Datum& left, const Datum& right, - ArithmeticOptions options = ArithmeticOptions(), - ExecContext* ctx = NULLPTR); - -/// \brief Subtract two values. Array values must be the same length. If the -/// minuend or subtrahend is null the result will be null. -/// -/// \param[in] left the value subtracted from (minuend) -/// \param[in] right the value by which the minuend is reduced (subtrahend) -/// \param[in] options arithmetic options (overflow handling), optional -/// \param[in] ctx the function execution context, optional -/// \return the elementwise difference -ARROW_EXPORT -Result<Datum> Subtract(const Datum& left, const Datum& right, - ArithmeticOptions options = ArithmeticOptions(), - ExecContext* ctx = NULLPTR); - -/// \brief Multiply two values. Array values must be the same length. If either -/// factor is null the result will be null. -/// -/// \param[in] left the first factor -/// \param[in] right the second factor -/// \param[in] options arithmetic options (overflow handling), optional -/// \param[in] ctx the function execution context, optional -/// \return the elementwise product -ARROW_EXPORT -Result<Datum> Multiply(const Datum& left, const Datum& right, - ArithmeticOptions options = ArithmeticOptions(), - ExecContext* ctx = NULLPTR); - -/// \brief Divide two values. Array values must be the same length. If either -/// argument is null the result will be null. For integer types, if there is -/// a zero divisor, an error will be raised. -/// -/// \param[in] left the dividend -/// \param[in] right the divisor -/// \param[in] options arithmetic options (enable/disable overflow checking), optional -/// \param[in] ctx the function execution context, optional -/// \return the elementwise quotient -ARROW_EXPORT -Result<Datum> Divide(const Datum& left, const Datum& right, - ArithmeticOptions options = ArithmeticOptions(), - ExecContext* ctx = NULLPTR); - +/// \brief Add two values together. Array values must be the same length. If +/// either addend is null the result will be null. +/// +/// \param[in] left the first addend +/// \param[in] right the second addend +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise sum +ARROW_EXPORT +Result<Datum> Add(const Datum& left, const Datum& right, + ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Subtract two values. Array values must be the same length. If the +/// minuend or subtrahend is null the result will be null. +/// +/// \param[in] left the value subtracted from (minuend) +/// \param[in] right the value by which the minuend is reduced (subtrahend) +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise difference +ARROW_EXPORT +Result<Datum> Subtract(const Datum& left, const Datum& right, + ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Multiply two values. Array values must be the same length. If either +/// factor is null the result will be null. +/// +/// \param[in] left the first factor +/// \param[in] right the second factor +/// \param[in] options arithmetic options (overflow handling), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise product +ARROW_EXPORT +Result<Datum> Multiply(const Datum& left, const Datum& right, + ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + +/// \brief Divide two values. Array values must be the same length. If either +/// argument is null the result will be null. For integer types, if there is +/// a zero divisor, an error will be raised. +/// +/// \param[in] left the dividend +/// \param[in] right the divisor +/// \param[in] options arithmetic options (enable/disable overflow checking), optional +/// \param[in] ctx the function execution context, optional +/// \return the elementwise quotient +ARROW_EXPORT +Result<Datum> Divide(const Datum& left, const Datum& right, + ArithmeticOptions options = ArithmeticOptions(), + ExecContext* ctx = NULLPTR); + /// \brief Negate values. /// /// If argument is null the result will be null. @@ -549,98 +549,98 @@ Result<Datum> MinElementWise( ARROW_EXPORT Result<Datum> Sign(const Datum& arg, ExecContext* ctx = NULLPTR); -/// \brief Compare a numeric array with a scalar. -/// -/// \param[in] left datum to compare, must be an Array -/// \param[in] right datum to compare, must be a Scalar of the same type than -/// left Datum. -/// \param[in] options compare options -/// \param[in] ctx the function execution context, optional -/// \return resulting datum -/// -/// Note on floating point arrays, this uses ieee-754 compare semantics. -/// -/// \since 1.0.0 -/// \note API not yet finalized +/// \brief Compare a numeric array with a scalar. +/// +/// \param[in] left datum to compare, must be an Array +/// \param[in] right datum to compare, must be a Scalar of the same type than +/// left Datum. +/// \param[in] options compare options +/// \param[in] ctx the function execution context, optional +/// \return resulting datum +/// +/// Note on floating point arrays, this uses ieee-754 compare semantics. +/// +/// \since 1.0.0 +/// \note API not yet finalized ARROW_DEPRECATED("Deprecated in 5.0.0. Use each compare function directly") -ARROW_EXPORT +ARROW_EXPORT Result<Datum> Compare(const Datum& left, const Datum& right, CompareOptions options, ExecContext* ctx = NULLPTR); - -/// \brief Invert the values of a boolean datum -/// \param[in] value datum to invert -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Invert(const Datum& value, ExecContext* ctx = NULLPTR); - -/// \brief Element-wise AND of two boolean datums which always propagates nulls -/// (null and false is null). -/// + +/// \brief Invert the values of a boolean datum +/// \param[in] value datum to invert +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Invert(const Datum& value, ExecContext* ctx = NULLPTR); + +/// \brief Element-wise AND of two boolean datums which always propagates nulls +/// (null and false is null). +/// /// \param[in] left left operand /// \param[in] right right operand -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> And(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); - -/// \brief Element-wise AND of two boolean datums with a Kleene truth table -/// (null and false is false). -/// +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> And(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); + +/// \brief Element-wise AND of two boolean datums with a Kleene truth table +/// (null and false is false). +/// /// \param[in] left left operand /// \param[in] right right operand -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> KleeneAnd(const Datum& left, const Datum& right, - ExecContext* ctx = NULLPTR); - -/// \brief Element-wise OR of two boolean datums which always propagates nulls -/// (null and true is null). -/// +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> KleeneAnd(const Datum& left, const Datum& right, + ExecContext* ctx = NULLPTR); + +/// \brief Element-wise OR of two boolean datums which always propagates nulls +/// (null and true is null). +/// /// \param[in] left left operand /// \param[in] right right operand -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Or(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); - -/// \brief Element-wise OR of two boolean datums with a Kleene truth table -/// (null or true is true). -/// +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Or(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); + +/// \brief Element-wise OR of two boolean datums with a Kleene truth table +/// (null or true is true). +/// /// \param[in] left left operand /// \param[in] right right operand -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> KleeneOr(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); - -/// \brief Element-wise XOR of two boolean datums +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> KleeneOr(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); + +/// \brief Element-wise XOR of two boolean datums /// \param[in] left left operand /// \param[in] right right operand -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Xor(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); - +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Xor(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); + /// \brief Element-wise AND NOT of two boolean datums which always propagates nulls /// (null and not true is null). /// @@ -668,73 +668,73 @@ ARROW_EXPORT Result<Datum> KleeneAndNot(const Datum& left, const Datum& right, ExecContext* ctx = NULLPTR); -/// \brief IsIn returns true for each element of `values` that is contained in -/// `value_set` -/// +/// \brief IsIn returns true for each element of `values` that is contained in +/// `value_set` +/// /// Behaviour of nulls is governed by SetLookupOptions::skip_nulls. -/// -/// \param[in] values array-like input to look up in value_set +/// +/// \param[in] values array-like input to look up in value_set /// \param[in] options SetLookupOptions -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> IsIn(const Datum& values, const SetLookupOptions& options, ExecContext* ctx = NULLPTR); ARROW_EXPORT -Result<Datum> IsIn(const Datum& values, const Datum& value_set, - ExecContext* ctx = NULLPTR); - -/// \brief IndexIn examines each slot in the values against a value_set array. -/// If the value is not found in value_set, null will be output. -/// If found, the index of occurrence within value_set (ignoring duplicates) -/// will be output. -/// -/// For example given values = [99, 42, 3, null] and -/// value_set = [3, 3, 99], the output will be = [1, null, 0, null] -/// +Result<Datum> IsIn(const Datum& values, const Datum& value_set, + ExecContext* ctx = NULLPTR); + +/// \brief IndexIn examines each slot in the values against a value_set array. +/// If the value is not found in value_set, null will be output. +/// If found, the index of occurrence within value_set (ignoring duplicates) +/// will be output. +/// +/// For example given values = [99, 42, 3, null] and +/// value_set = [3, 3, 99], the output will be = [1, null, 0, null] +/// /// Behaviour of nulls is governed by SetLookupOptions::skip_nulls. -/// -/// \param[in] values array-like input +/// +/// \param[in] values array-like input /// \param[in] options SetLookupOptions -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> IndexIn(const Datum& values, const SetLookupOptions& options, ExecContext* ctx = NULLPTR); ARROW_EXPORT -Result<Datum> IndexIn(const Datum& values, const Datum& value_set, - ExecContext* ctx = NULLPTR); - -/// \brief IsValid returns true for each element of `values` that is not null, -/// false otherwise -/// -/// \param[in] values input to examine for validity -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> IsValid(const Datum& values, ExecContext* ctx = NULLPTR); - -/// \brief IsNull returns true for each element of `values` that is null, -/// false otherwise -/// -/// \param[in] values input to examine for nullity -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> IsNull(const Datum& values, ExecContext* ctx = NULLPTR); - +Result<Datum> IndexIn(const Datum& values, const Datum& value_set, + ExecContext* ctx = NULLPTR); + +/// \brief IsValid returns true for each element of `values` that is not null, +/// false otherwise +/// +/// \param[in] values input to examine for validity +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> IsValid(const Datum& values, ExecContext* ctx = NULLPTR); + +/// \brief IsNull returns true for each element of `values` that is null, +/// false otherwise +/// +/// \param[in] values input to examine for nullity +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> IsNull(const Datum& values, ExecContext* ctx = NULLPTR); + /// \brief IsNan returns true for each element of `values` that is NaN, /// false otherwise /// @@ -747,21 +747,21 @@ Result<Datum> IsNull(const Datum& values, ExecContext* ctx = NULLPTR); ARROW_EXPORT Result<Datum> IsNan(const Datum& values, ExecContext* ctx = NULLPTR); -/// \brief FillNull replaces each null element in `values` -/// with `fill_value` -/// -/// \param[in] values input to examine for nullity -/// \param[in] fill_value scalar -/// \param[in] ctx the function execution context, optional -/// -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> FillNull(const Datum& values, const Datum& fill_value, - ExecContext* ctx = NULLPTR); - +/// \brief FillNull replaces each null element in `values` +/// with `fill_value` +/// +/// \param[in] values input to examine for nullity +/// \param[in] fill_value scalar +/// \param[in] ctx the function execution context, optional +/// +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> FillNull(const Datum& values, const Datum& fill_value, + ExecContext* ctx = NULLPTR); + /// \brief IfElse returns elements chosen from `left` or `right` /// depending on `cond`. `null` values in `cond` will be promoted to the result /// @@ -985,5 +985,5 @@ Result<Datum> Nanosecond(const Datum& values, ExecContext* ctx = NULLPTR); /// \note API not yet finalized ARROW_EXPORT Result<Datum> Subsecond(const Datum& values, ExecContext* ctx = NULLPTR); -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.cc index 967829f425..a68969b2ee 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.cc @@ -1,43 +1,43 @@ -// 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. - -#include "arrow/compute/api_vector.h" - -#include <memory> +// 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. + +#include "arrow/compute/api_vector.h" + +#include <memory> #include <sstream> -#include <utility> -#include <vector> - -#include "arrow/array/array_nested.h" -#include "arrow/array/builder_primitive.h" -#include "arrow/compute/exec.h" +#include <utility> +#include <vector> + +#include "arrow/array/array_nested.h" +#include "arrow/array/builder_primitive.h" +#include "arrow/compute/exec.h" #include "arrow/compute/function_internal.h" #include "arrow/compute/registry.h" -#include "arrow/datum.h" -#include "arrow/record_batch.h" -#include "arrow/result.h" -#include "arrow/util/checked_cast.h" +#include "arrow/datum.h" +#include "arrow/record_batch.h" +#include "arrow/result.h" +#include "arrow/util/checked_cast.h" #include "arrow/util/logging.h" - -namespace arrow { - + +namespace arrow { + using internal::checked_cast; -using internal::checked_pointer_cast; - +using internal::checked_pointer_cast; + namespace internal { using compute::DictionaryEncodeOptions; using compute::FilterOptions; @@ -73,9 +73,9 @@ struct EnumTraits<DictionaryEncodeOptions::NullEncodingBehavior> }; } // namespace internal -namespace compute { - -// ---------------------------------------------------------------------- +namespace compute { + +// ---------------------------------------------------------------------- // Function options bool SortKey::Equals(const SortKey& other) const { @@ -152,16 +152,16 @@ void RegisterVectorOptions(FunctionRegistry* registry) { } // namespace internal // ---------------------------------------------------------------------- -// Direct exec interface to kernels - -Result<std::shared_ptr<Array>> NthToIndices(const Array& values, int64_t n, - ExecContext* ctx) { - PartitionNthOptions options(/*pivot=*/n); - ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("partition_nth_indices", - {Datum(values)}, &options, ctx)); - return result.make_array(); -} - +// Direct exec interface to kernels + +Result<std::shared_ptr<Array>> NthToIndices(const Array& values, int64_t n, + ExecContext* ctx) { + PartitionNthOptions options(/*pivot=*/n); + ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("partition_nth_indices", + {Datum(values)}, &options, ctx)); + return result.make_array(); +} + Result<Datum> ReplaceWithMask(const Datum& values, const Datum& mask, const Datum& replacements, ExecContext* ctx) { return CallFunction("replace_with_mask", {values, mask, replacements}, ctx); @@ -172,9 +172,9 @@ Result<std::shared_ptr<Array>> SortIndices(const Array& values, SortOrder order, ArraySortOptions options(order); ARROW_ASSIGN_OR_RAISE( Datum result, CallFunction("array_sort_indices", {Datum(values)}, &options, ctx)); - return result.make_array(); -} - + return result.make_array(); +} + Result<std::shared_ptr<Array>> SortIndices(const ChunkedArray& chunked_array, SortOrder order, ExecContext* ctx) { SortOptions options({SortKey("not-used", order)}); @@ -190,94 +190,94 @@ Result<std::shared_ptr<Array>> SortIndices(const Datum& datum, const SortOptions return result.make_array(); } -Result<std::shared_ptr<Array>> Unique(const Datum& value, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("unique", {value}, ctx)); - return result.make_array(); -} - +Result<std::shared_ptr<Array>> Unique(const Datum& value, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("unique", {value}, ctx)); + return result.make_array(); +} + Result<Datum> DictionaryEncode(const Datum& value, const DictionaryEncodeOptions& options, ExecContext* ctx) { return CallFunction("dictionary_encode", {value}, &options, ctx); -} - -const char kValuesFieldName[] = "values"; -const char kCountsFieldName[] = "counts"; -const int32_t kValuesFieldIndex = 0; -const int32_t kCountsFieldIndex = 1; - -Result<std::shared_ptr<StructArray>> ValueCounts(const Datum& value, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("value_counts", {value}, ctx)); - return checked_pointer_cast<StructArray>(result.make_array()); -} - -// ---------------------------------------------------------------------- -// Filter- and take-related selection functions - -Result<Datum> Filter(const Datum& values, const Datum& filter, - const FilterOptions& options, ExecContext* ctx) { - // Invoke metafunction which deals with Datum kinds other than just Array, - // ChunkedArray. - return CallFunction("filter", {values, filter}, &options, ctx); -} - -Result<Datum> Take(const Datum& values, const Datum& filter, const TakeOptions& options, - ExecContext* ctx) { - // Invoke metafunction which deals with Datum kinds other than just Array, - // ChunkedArray. - return CallFunction("take", {values, filter}, &options, ctx); -} - -Result<std::shared_ptr<Array>> Take(const Array& values, const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum out, Take(Datum(values), Datum(indices), options, ctx)); - return out.make_array(); -} - -// ---------------------------------------------------------------------- -// Deprecated functions - -Result<std::shared_ptr<ChunkedArray>> Take(const ChunkedArray& values, - const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); - return result.chunked_array(); -} - -Result<std::shared_ptr<ChunkedArray>> Take(const ChunkedArray& values, - const ChunkedArray& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); - return result.chunked_array(); -} - -Result<std::shared_ptr<ChunkedArray>> Take(const Array& values, - const ChunkedArray& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); - return result.chunked_array(); -} - -Result<std::shared_ptr<RecordBatch>> Take(const RecordBatch& batch, const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(batch), Datum(indices), options, ctx)); - return result.record_batch(); -} - -Result<std::shared_ptr<Table>> Take(const Table& table, const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(table), Datum(indices), options, ctx)); - return result.table(); -} - -Result<std::shared_ptr<Table>> Take(const Table& table, const ChunkedArray& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(table), Datum(indices), options, ctx)); - return result.table(); -} - +} + +const char kValuesFieldName[] = "values"; +const char kCountsFieldName[] = "counts"; +const int32_t kValuesFieldIndex = 0; +const int32_t kCountsFieldIndex = 1; + +Result<std::shared_ptr<StructArray>> ValueCounts(const Datum& value, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, CallFunction("value_counts", {value}, ctx)); + return checked_pointer_cast<StructArray>(result.make_array()); +} + +// ---------------------------------------------------------------------- +// Filter- and take-related selection functions + +Result<Datum> Filter(const Datum& values, const Datum& filter, + const FilterOptions& options, ExecContext* ctx) { + // Invoke metafunction which deals with Datum kinds other than just Array, + // ChunkedArray. + return CallFunction("filter", {values, filter}, &options, ctx); +} + +Result<Datum> Take(const Datum& values, const Datum& filter, const TakeOptions& options, + ExecContext* ctx) { + // Invoke metafunction which deals with Datum kinds other than just Array, + // ChunkedArray. + return CallFunction("take", {values, filter}, &options, ctx); +} + +Result<std::shared_ptr<Array>> Take(const Array& values, const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum out, Take(Datum(values), Datum(indices), options, ctx)); + return out.make_array(); +} + +// ---------------------------------------------------------------------- +// Deprecated functions + +Result<std::shared_ptr<ChunkedArray>> Take(const ChunkedArray& values, + const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); + return result.chunked_array(); +} + +Result<std::shared_ptr<ChunkedArray>> Take(const ChunkedArray& values, + const ChunkedArray& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); + return result.chunked_array(); +} + +Result<std::shared_ptr<ChunkedArray>> Take(const Array& values, + const ChunkedArray& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(values), Datum(indices), options, ctx)); + return result.chunked_array(); +} + +Result<std::shared_ptr<RecordBatch>> Take(const RecordBatch& batch, const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(batch), Datum(indices), options, ctx)); + return result.record_batch(); +} + +Result<std::shared_ptr<Table>> Take(const Table& table, const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(table), Datum(indices), options, ctx)); + return result.table(); +} + +Result<std::shared_ptr<Table>> Take(const Table& table, const ChunkedArray& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Take(Datum(table), Datum(indices), options, ctx)); + return result.table(); +} + Result<std::shared_ptr<Array>> SortToIndices(const Array& values, ExecContext* ctx) { return SortIndices(values, SortOrder::Ascending, ctx); } -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.h index 4a804e4a57..9d8d4271db 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/api_vector.h @@ -1,65 +1,65 @@ -// 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 <memory> - -#include "arrow/compute/function.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/type_fwd.h" - -namespace arrow { -namespace compute { - -class ExecContext; - -/// \addtogroup compute-concrete-options -/// @{ - +// 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 <memory> + +#include "arrow/compute/function.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/type_fwd.h" + +namespace arrow { +namespace compute { + +class ExecContext; + +/// \addtogroup compute-concrete-options +/// @{ + class ARROW_EXPORT FilterOptions : public FunctionOptions { public: - /// Configure the action taken when a slot of the selection mask is null - enum NullSelectionBehavior { - /// the corresponding filtered value will be removed in the output - DROP, - /// the corresponding filtered value will be null in the output - EMIT_NULL, - }; - + /// Configure the action taken when a slot of the selection mask is null + enum NullSelectionBehavior { + /// the corresponding filtered value will be removed in the output + DROP, + /// the corresponding filtered value will be null in the output + EMIT_NULL, + }; + explicit FilterOptions(NullSelectionBehavior null_selection = DROP); constexpr static char const kTypeName[] = "FilterOptions"; - static FilterOptions Defaults() { return FilterOptions(); } - - NullSelectionBehavior null_selection_behavior = DROP; -}; - + static FilterOptions Defaults() { return FilterOptions(); } + + NullSelectionBehavior null_selection_behavior = DROP; +}; + class ARROW_EXPORT TakeOptions : public FunctionOptions { public: explicit TakeOptions(bool boundscheck = true); constexpr static char const kTypeName[] = "TakeOptions"; - static TakeOptions BoundsCheck() { return TakeOptions(true); } - static TakeOptions NoBoundsCheck() { return TakeOptions(false); } - static TakeOptions Defaults() { return BoundsCheck(); } + static TakeOptions BoundsCheck() { return TakeOptions(true); } + static TakeOptions NoBoundsCheck() { return TakeOptions(false); } + static TakeOptions Defaults() { return BoundsCheck(); } bool boundscheck = true; -}; - +}; + /// \brief Options for the dictionary encode function class ARROW_EXPORT DictionaryEncodeOptions : public FunctionOptions { public: @@ -119,58 +119,58 @@ class ARROW_EXPORT SortOptions : public FunctionOptions { std::vector<SortKey> sort_keys; }; -/// \brief Partitioning options for NthToIndices +/// \brief Partitioning options for NthToIndices class ARROW_EXPORT PartitionNthOptions : public FunctionOptions { public: explicit PartitionNthOptions(int64_t pivot); PartitionNthOptions() : PartitionNthOptions(0) {} constexpr static char const kTypeName[] = "PartitionNthOptions"; - - /// The index into the equivalent sorted array of the partition pivot element. - int64_t pivot; -}; - -/// @} - -/// \brief Filter with a boolean selection filter -/// -/// The output will be populated with values from the input at positions -/// where the selection filter is not 0. Nulls in the filter will be handled -/// based on options.null_selection_behavior. -/// -/// For example given values = ["a", "b", "c", null, "e", "f"] and -/// filter = [0, 1, 1, 0, null, 1], the output will be -/// (null_selection_behavior == DROP) = ["b", "c", "f"] -/// (null_selection_behavior == EMIT_NULL) = ["b", "c", null, "f"] -/// -/// \param[in] values array to filter -/// \param[in] filter indicates which values should be filtered out -/// \param[in] options configures null_selection_behavior -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -ARROW_EXPORT -Result<Datum> Filter(const Datum& values, const Datum& filter, - const FilterOptions& options = FilterOptions::Defaults(), - ExecContext* ctx = NULLPTR); - -namespace internal { - -// These internal functions are implemented in kernels/vector_selection.cc - -/// \brief Return the number of selected indices in the boolean filter -ARROW_EXPORT -int64_t GetFilterOutputSize(const ArrayData& filter, - FilterOptions::NullSelectionBehavior null_selection); - -/// \brief Compute uint64 selection indices for use with Take given a boolean -/// filter -ARROW_EXPORT -Result<std::shared_ptr<ArrayData>> GetTakeIndices( - const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, - MemoryPool* memory_pool = default_memory_pool()); - -} // namespace internal - + + /// The index into the equivalent sorted array of the partition pivot element. + int64_t pivot; +}; + +/// @} + +/// \brief Filter with a boolean selection filter +/// +/// The output will be populated with values from the input at positions +/// where the selection filter is not 0. Nulls in the filter will be handled +/// based on options.null_selection_behavior. +/// +/// For example given values = ["a", "b", "c", null, "e", "f"] and +/// filter = [0, 1, 1, 0, null, 1], the output will be +/// (null_selection_behavior == DROP) = ["b", "c", "f"] +/// (null_selection_behavior == EMIT_NULL) = ["b", "c", null, "f"] +/// +/// \param[in] values array to filter +/// \param[in] filter indicates which values should be filtered out +/// \param[in] options configures null_selection_behavior +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +ARROW_EXPORT +Result<Datum> Filter(const Datum& values, const Datum& filter, + const FilterOptions& options = FilterOptions::Defaults(), + ExecContext* ctx = NULLPTR); + +namespace internal { + +// These internal functions are implemented in kernels/vector_selection.cc + +/// \brief Return the number of selected indices in the boolean filter +ARROW_EXPORT +int64_t GetFilterOutputSize(const ArrayData& filter, + FilterOptions::NullSelectionBehavior null_selection); + +/// \brief Compute uint64 selection indices for use with Take given a boolean +/// filter +ARROW_EXPORT +Result<std::shared_ptr<ArrayData>> GetTakeIndices( + const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, + MemoryPool* memory_pool = default_memory_pool()); + +} // namespace internal + /// \brief ReplaceWithMask replaces each value in the array corresponding /// to a true value in the mask with the next element from `replacements`. /// @@ -188,72 +188,72 @@ ARROW_EXPORT Result<Datum> ReplaceWithMask(const Datum& values, const Datum& mask, const Datum& replacements, ExecContext* ctx = NULLPTR); -/// \brief Take from an array of values at indices in another array -/// -/// The output array will be of the same type as the input values -/// array, with elements taken from the values array at the given -/// indices. If an index is null then the taken element will be null. -/// -/// For example given values = ["a", "b", "c", null, "e", "f"] and -/// indices = [2, 1, null, 3], the output will be -/// = [values[2], values[1], null, values[3]] -/// = ["c", "b", null, null] -/// -/// \param[in] values datum from which to take -/// \param[in] indices which values to take -/// \param[in] options options -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -ARROW_EXPORT -Result<Datum> Take(const Datum& values, const Datum& indices, - const TakeOptions& options = TakeOptions::Defaults(), - ExecContext* ctx = NULLPTR); - -/// \brief Take with Array inputs and output -ARROW_EXPORT -Result<std::shared_ptr<Array>> Take(const Array& values, const Array& indices, - const TakeOptions& options = TakeOptions::Defaults(), - ExecContext* ctx = NULLPTR); - -/// \brief Returns indices that partition an array around n-th -/// sorted element. -/// -/// Find index of n-th(0 based) smallest value and perform indirect -/// partition of an array around that element. Output indices[0 ~ n-1] -/// holds values no greater than n-th element, and indices[n+1 ~ end] -/// holds values no less than n-th element. Elements in each partition -/// is not sorted. Nulls will be partitioned to the end of the output. -/// Output is not guaranteed to be stable. -/// -/// \param[in] values array to be partitioned -/// \param[in] n pivot array around sorted n-th element -/// \param[in] ctx the function execution context, optional -/// \return offsets indices that would partition an array -ARROW_EXPORT -Result<std::shared_ptr<Array>> NthToIndices(const Array& values, int64_t n, - ExecContext* ctx = NULLPTR); - +/// \brief Take from an array of values at indices in another array +/// +/// The output array will be of the same type as the input values +/// array, with elements taken from the values array at the given +/// indices. If an index is null then the taken element will be null. +/// +/// For example given values = ["a", "b", "c", null, "e", "f"] and +/// indices = [2, 1, null, 3], the output will be +/// = [values[2], values[1], null, values[3]] +/// = ["c", "b", null, null] +/// +/// \param[in] values datum from which to take +/// \param[in] indices which values to take +/// \param[in] options options +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +ARROW_EXPORT +Result<Datum> Take(const Datum& values, const Datum& indices, + const TakeOptions& options = TakeOptions::Defaults(), + ExecContext* ctx = NULLPTR); + +/// \brief Take with Array inputs and output +ARROW_EXPORT +Result<std::shared_ptr<Array>> Take(const Array& values, const Array& indices, + const TakeOptions& options = TakeOptions::Defaults(), + ExecContext* ctx = NULLPTR); + +/// \brief Returns indices that partition an array around n-th +/// sorted element. +/// +/// Find index of n-th(0 based) smallest value and perform indirect +/// partition of an array around that element. Output indices[0 ~ n-1] +/// holds values no greater than n-th element, and indices[n+1 ~ end] +/// holds values no less than n-th element. Elements in each partition +/// is not sorted. Nulls will be partitioned to the end of the output. +/// Output is not guaranteed to be stable. +/// +/// \param[in] values array to be partitioned +/// \param[in] n pivot array around sorted n-th element +/// \param[in] ctx the function execution context, optional +/// \return offsets indices that would partition an array +ARROW_EXPORT +Result<std::shared_ptr<Array>> NthToIndices(const Array& values, int64_t n, + ExecContext* ctx = NULLPTR); + /// \brief Returns the indices that would sort an array in the /// specified order. -/// -/// Perform an indirect sort of array. The output array will contain -/// indices that would sort an array, which would be the same length +/// +/// Perform an indirect sort of array. The output array will contain +/// indices that would sort an array, which would be the same length /// as input. Nulls will be stably partitioned to the end of the output /// regardless of order. -/// +/// /// For example given array = [null, 1, 3.3, null, 2, 5.3] and order /// = SortOrder::DESCENDING, the output will be [5, 2, 4, 1, 0, /// 3]. -/// +/// /// \param[in] array array to sort /// \param[in] order ascending or descending -/// \param[in] ctx the function execution context, optional -/// \return offsets indices that would sort an array -ARROW_EXPORT +/// \param[in] ctx the function execution context, optional +/// \return offsets indices that would sort an array +ARROW_EXPORT Result<std::shared_ptr<Array>> SortIndices(const Array& array, SortOrder order = SortOrder::Ascending, ExecContext* ctx = NULLPTR); - + /// \brief Returns the indices that would sort a chunked array in the /// specified order. /// @@ -300,44 +300,44 @@ ARROW_EXPORT Result<std::shared_ptr<Array>> SortIndices(const Datum& datum, const SortOptions& options, ExecContext* ctx = NULLPTR); -/// \brief Compute unique elements from an array-like object -/// -/// Note if a null occurs in the input it will NOT be included in the output. -/// -/// \param[in] datum array-like input -/// \param[in] ctx the function execution context, optional -/// \return result as Array -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<std::shared_ptr<Array>> Unique(const Datum& datum, ExecContext* ctx = NULLPTR); - -// Constants for accessing the output of ValueCounts -ARROW_EXPORT extern const char kValuesFieldName[]; -ARROW_EXPORT extern const char kCountsFieldName[]; -ARROW_EXPORT extern const int32_t kValuesFieldIndex; -ARROW_EXPORT extern const int32_t kCountsFieldIndex; - -/// \brief Return counts of unique elements from an array-like object. -/// -/// Note that the counts do not include counts for nulls in the array. These can be -/// obtained separately from metadata. -/// -/// For floating point arrays there is no attempt to normalize -0.0, 0.0 and NaN values -/// which can lead to unexpected results if the input Array has these values. -/// -/// \param[in] value array-like input -/// \param[in] ctx the function execution context, optional -/// \return counts An array of <input type "Values", int64_t "Counts"> structs. -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<std::shared_ptr<StructArray>> ValueCounts(const Datum& value, - ExecContext* ctx = NULLPTR); - -/// \brief Dictionary-encode values in an array-like object +/// \brief Compute unique elements from an array-like object +/// +/// Note if a null occurs in the input it will NOT be included in the output. +/// +/// \param[in] datum array-like input +/// \param[in] ctx the function execution context, optional +/// \return result as Array +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<std::shared_ptr<Array>> Unique(const Datum& datum, ExecContext* ctx = NULLPTR); + +// Constants for accessing the output of ValueCounts +ARROW_EXPORT extern const char kValuesFieldName[]; +ARROW_EXPORT extern const char kCountsFieldName[]; +ARROW_EXPORT extern const int32_t kValuesFieldIndex; +ARROW_EXPORT extern const int32_t kCountsFieldIndex; + +/// \brief Return counts of unique elements from an array-like object. +/// +/// Note that the counts do not include counts for nulls in the array. These can be +/// obtained separately from metadata. +/// +/// For floating point arrays there is no attempt to normalize -0.0, 0.0 and NaN values +/// which can lead to unexpected results if the input Array has these values. +/// +/// \param[in] value array-like input +/// \param[in] ctx the function execution context, optional +/// \return counts An array of <input type "Values", int64_t "Counts"> structs. +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<std::shared_ptr<StructArray>> ValueCounts(const Datum& value, + ExecContext* ctx = NULLPTR); + +/// \brief Dictionary-encode values in an array-like object /// /// Any nulls encountered in the dictionary will be handled according to the /// specified null encoding behavior. @@ -349,62 +349,62 @@ Result<std::shared_ptr<StructArray>> ValueCounts(const Datum& value, /// If the input is already dictionary encoded this function is a no-op unless /// it needs to modify the null_encoding (TODO) /// -/// \param[in] data array-like input -/// \param[in] ctx the function execution context, optional +/// \param[in] data array-like input +/// \param[in] ctx the function execution context, optional /// \param[in] options configures null encoding behavior -/// \return result with same shape and type as input -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT +/// \return result with same shape and type as input +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT Result<Datum> DictionaryEncode( const Datum& data, const DictionaryEncodeOptions& options = DictionaryEncodeOptions::Defaults(), ExecContext* ctx = NULLPTR); - -// ---------------------------------------------------------------------- -// Deprecated functions - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<ChunkedArray>> Take( - const ChunkedArray& values, const Array& indices, - const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<ChunkedArray>> Take( - const ChunkedArray& values, const ChunkedArray& indices, - const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<ChunkedArray>> Take( - const Array& values, const ChunkedArray& indices, - const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<RecordBatch>> Take( - const RecordBatch& batch, const Array& indices, - const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<Table>> Take(const Table& table, const Array& indices, - const TakeOptions& options = TakeOptions::Defaults(), - ExecContext* context = NULLPTR); - -ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") -ARROW_EXPORT -Result<std::shared_ptr<Table>> Take(const Table& table, const ChunkedArray& indices, - const TakeOptions& options = TakeOptions::Defaults(), - ExecContext* context = NULLPTR); - + +// ---------------------------------------------------------------------- +// Deprecated functions + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<ChunkedArray>> Take( + const ChunkedArray& values, const Array& indices, + const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<ChunkedArray>> Take( + const ChunkedArray& values, const ChunkedArray& indices, + const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<ChunkedArray>> Take( + const Array& values, const ChunkedArray& indices, + const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<RecordBatch>> Take( + const RecordBatch& batch, const Array& indices, + const TakeOptions& options = TakeOptions::Defaults(), ExecContext* context = NULLPTR); + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<Table>> Take(const Table& table, const Array& indices, + const TakeOptions& options = TakeOptions::Defaults(), + ExecContext* context = NULLPTR); + +ARROW_DEPRECATED("Deprecated in 1.0.0. Use Datum-based version") +ARROW_EXPORT +Result<std::shared_ptr<Table>> Take(const Table& table, const ChunkedArray& indices, + const TakeOptions& options = TakeOptions::Defaults(), + ExecContext* context = NULLPTR); + ARROW_DEPRECATED("Deprecated in 3.0.0. Use SortIndices()") ARROW_EXPORT Result<std::shared_ptr<Array>> SortToIndices(const Array& values, ExecContext* ctx = NULLPTR); -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.cc index d92079cde8..4de68ba8d9 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.cc @@ -1,128 +1,128 @@ -// 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. - -#include "arrow/compute/cast.h" - -#include <mutex> +// 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. + +#include "arrow/compute/cast.h" + +#include <mutex> #include <sstream> -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "arrow/compute/cast_internal.h" -#include "arrow/compute/exec.h" +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include "arrow/compute/cast_internal.h" +#include "arrow/compute/exec.h" #include "arrow/compute/function_internal.h" -#include "arrow/compute/kernel.h" -#include "arrow/compute/kernels/codegen_internal.h" -#include "arrow/compute/registry.h" -#include "arrow/util/logging.h" +#include "arrow/compute/kernel.h" +#include "arrow/compute/kernels/codegen_internal.h" +#include "arrow/compute/registry.h" +#include "arrow/util/logging.h" #include "arrow/util/reflection_internal.h" - -namespace arrow { - -using internal::ToTypeName; - -namespace compute { -namespace internal { - + +namespace arrow { + +using internal::ToTypeName; + +namespace compute { +namespace internal { + // ---------------------------------------------------------------------- // Function options namespace { -std::unordered_map<int, std::shared_ptr<CastFunction>> g_cast_table; +std::unordered_map<int, std::shared_ptr<CastFunction>> g_cast_table; std::once_flag cast_table_initialized; - -void AddCastFunctions(const std::vector<std::shared_ptr<CastFunction>>& funcs) { - for (const auto& func : funcs) { - g_cast_table[static_cast<int>(func->out_type_id())] = func; - } -} - -void InitCastTable() { - AddCastFunctions(GetBooleanCasts()); - AddCastFunctions(GetBinaryLikeCasts()); - AddCastFunctions(GetNestedCasts()); - AddCastFunctions(GetNumericCasts()); - AddCastFunctions(GetTemporalCasts()); + +void AddCastFunctions(const std::vector<std::shared_ptr<CastFunction>>& funcs) { + for (const auto& func : funcs) { + g_cast_table[static_cast<int>(func->out_type_id())] = func; + } +} + +void InitCastTable() { + AddCastFunctions(GetBooleanCasts()); + AddCastFunctions(GetBinaryLikeCasts()); + AddCastFunctions(GetNestedCasts()); + AddCastFunctions(GetNumericCasts()); + AddCastFunctions(GetTemporalCasts()); AddCastFunctions(GetDictionaryCasts()); -} - -void EnsureInitCastTable() { std::call_once(cast_table_initialized, InitCastTable); } - -// Private version of GetCastFunction with better error reporting -// if the input type is known. -Result<std::shared_ptr<CastFunction>> GetCastFunctionInternal( - const std::shared_ptr<DataType>& to_type, const DataType* from_type = nullptr) { - internal::EnsureInitCastTable(); - auto it = internal::g_cast_table.find(static_cast<int>(to_type->id())); - if (it == internal::g_cast_table.end()) { - if (from_type != nullptr) { - return Status::NotImplemented("Unsupported cast from ", *from_type, " to ", - *to_type, - " (no available cast function for target type)"); - } else { - return Status::NotImplemented("Unsupported cast to ", *to_type, - " (no available cast function for target type)"); - } - } - return it->second; -} - +} + +void EnsureInitCastTable() { std::call_once(cast_table_initialized, InitCastTable); } + +// Private version of GetCastFunction with better error reporting +// if the input type is known. +Result<std::shared_ptr<CastFunction>> GetCastFunctionInternal( + const std::shared_ptr<DataType>& to_type, const DataType* from_type = nullptr) { + internal::EnsureInitCastTable(); + auto it = internal::g_cast_table.find(static_cast<int>(to_type->id())); + if (it == internal::g_cast_table.end()) { + if (from_type != nullptr) { + return Status::NotImplemented("Unsupported cast from ", *from_type, " to ", + *to_type, + " (no available cast function for target type)"); + } else { + return Status::NotImplemented("Unsupported cast to ", *to_type, + " (no available cast function for target type)"); + } + } + return it->second; +} + const FunctionDoc cast_doc{"Cast values to another data type", ("Behavior when values wouldn't fit in the target type\n" "can be controlled through CastOptions."), {"input"}, "CastOptions"}; - + // Metafunction for dispatching to appropriate CastFunction. This corresponds -// to the standard SQL CAST(expr AS target_type) -class CastMetaFunction : public MetaFunction { - public: +// to the standard SQL CAST(expr AS target_type) +class CastMetaFunction : public MetaFunction { + public: CastMetaFunction() : MetaFunction("cast", Arity::Unary(), &cast_doc) {} - - Result<const CastOptions*> ValidateOptions(const FunctionOptions* options) const { - auto cast_options = static_cast<const CastOptions*>(options); - - if (cast_options == nullptr || cast_options->to_type == nullptr) { - return Status::Invalid( - "Cast requires that options be passed with " - "the to_type populated"); - } - - return cast_options; - } - - Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const override { - ARROW_ASSIGN_OR_RAISE(auto cast_options, ValidateOptions(options)); - if (args[0].type()->Equals(*cast_options->to_type)) { - return args[0]; - } - ARROW_ASSIGN_OR_RAISE( - std::shared_ptr<CastFunction> cast_func, - GetCastFunctionInternal(cast_options->to_type, args[0].type().get())); - return cast_func->Execute(args, options, ctx); - } -}; - + + Result<const CastOptions*> ValidateOptions(const FunctionOptions* options) const { + auto cast_options = static_cast<const CastOptions*>(options); + + if (cast_options == nullptr || cast_options->to_type == nullptr) { + return Status::Invalid( + "Cast requires that options be passed with " + "the to_type populated"); + } + + return cast_options; + } + + Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const override { + ARROW_ASSIGN_OR_RAISE(auto cast_options, ValidateOptions(options)); + if (args[0].type()->Equals(*cast_options->to_type)) { + return args[0]; + } + ARROW_ASSIGN_OR_RAISE( + std::shared_ptr<CastFunction> cast_func, + GetCastFunctionInternal(cast_options->to_type, args[0].type().get())); + return cast_func->Execute(args, options, ctx); + } +}; + static auto kCastOptionsType = GetFunctionOptionsType<CastOptions>( arrow::internal::DataMember("to_type", &CastOptions::to_type), arrow::internal::DataMember("allow_int_overflow", &CastOptions::allow_int_overflow), @@ -135,12 +135,12 @@ static auto kCastOptionsType = GetFunctionOptionsType<CastOptions>( arrow::internal::DataMember("allow_invalid_utf8", &CastOptions::allow_invalid_utf8)); } // namespace -void RegisterScalarCast(FunctionRegistry* registry) { - DCHECK_OK(registry->AddFunction(std::make_shared<CastMetaFunction>())); +void RegisterScalarCast(FunctionRegistry* registry) { + DCHECK_OK(registry->AddFunction(std::make_shared<CastMetaFunction>())); DCHECK_OK(registry->AddFunctionOptionsType(kCastOptionsType)); -} -} // namespace internal - +} +} // namespace internal + CastOptions::CastOptions(bool safe) : FunctionOptions(internal::kCastOptionsType), allow_int_overflow(!safe), @@ -149,53 +149,53 @@ CastOptions::CastOptions(bool safe) allow_decimal_truncate(!safe), allow_float_truncate(!safe), allow_invalid_utf8(!safe) {} - + constexpr char CastOptions::kTypeName[]; - + CastFunction::CastFunction(std::string name, Type::type out_type_id) : ScalarFunction(std::move(name), Arity::Unary(), /*doc=*/nullptr), out_type_id_(out_type_id) {} - -Status CastFunction::AddKernel(Type::type in_type_id, ScalarKernel kernel) { - // We use the same KernelInit for every cast - kernel.init = internal::CastState::Init; - RETURN_NOT_OK(ScalarFunction::AddKernel(kernel)); + +Status CastFunction::AddKernel(Type::type in_type_id, ScalarKernel kernel) { + // We use the same KernelInit for every cast + kernel.init = internal::CastState::Init; + RETURN_NOT_OK(ScalarFunction::AddKernel(kernel)); in_type_ids_.push_back(in_type_id); - return Status::OK(); -} - -Status CastFunction::AddKernel(Type::type in_type_id, std::vector<InputType> in_types, - OutputType out_type, ArrayKernelExec exec, - NullHandling::type null_handling, - MemAllocation::type mem_allocation) { - ScalarKernel kernel; - kernel.signature = KernelSignature::Make(std::move(in_types), std::move(out_type)); - kernel.exec = exec; - kernel.null_handling = null_handling; - kernel.mem_allocation = mem_allocation; - return AddKernel(in_type_id, std::move(kernel)); -} - + return Status::OK(); +} + +Status CastFunction::AddKernel(Type::type in_type_id, std::vector<InputType> in_types, + OutputType out_type, ArrayKernelExec exec, + NullHandling::type null_handling, + MemAllocation::type mem_allocation) { + ScalarKernel kernel; + kernel.signature = KernelSignature::Make(std::move(in_types), std::move(out_type)); + kernel.exec = exec; + kernel.null_handling = null_handling; + kernel.mem_allocation = mem_allocation; + return AddKernel(in_type_id, std::move(kernel)); +} + Result<const Kernel*> CastFunction::DispatchExact( - const std::vector<ValueDescr>& values) const { + const std::vector<ValueDescr>& values) const { RETURN_NOT_OK(CheckArity(values)); - - std::vector<const ScalarKernel*> candidate_kernels; - for (const auto& kernel : kernels_) { - if (kernel.signature->MatchesInputs(values)) { - candidate_kernels.push_back(&kernel); - } - } - - if (candidate_kernels.size() == 0) { - return Status::NotImplemented("Unsupported cast from ", values[0].type->ToString(), + + std::vector<const ScalarKernel*> candidate_kernels; + for (const auto& kernel : kernels_) { + if (kernel.signature->MatchesInputs(values)) { + candidate_kernels.push_back(&kernel); + } + } + + if (candidate_kernels.size() == 0) { + return Status::NotImplemented("Unsupported cast from ", values[0].type->ToString(), " to ", ToTypeName(out_type_id_), " using function ", - this->name()); + this->name()); } if (candidate_kernels.size() == 1) { - // One match, return it - return candidate_kernels[0]; + // One match, return it + return candidate_kernels[0]; } // Now we are in a casting scenario where we may have both a EXACT_TYPE and @@ -206,41 +206,41 @@ Result<const Kernel*> CastFunction::DispatchExact( if (arg0.kind() == InputType::EXACT_TYPE) { // Bingo. Return it return kernel; - } - } + } + } // We didn't find an exact match. So just return some kernel that matches return candidate_kernels[0]; -} - -Result<Datum> Cast(const Datum& value, const CastOptions& options, ExecContext* ctx) { - return CallFunction("cast", {value}, &options, ctx); -} - -Result<Datum> Cast(const Datum& value, std::shared_ptr<DataType> to_type, - const CastOptions& options, ExecContext* ctx) { - CastOptions options_with_to_type = options; - options_with_to_type.to_type = to_type; - return Cast(value, options_with_to_type, ctx); -} - -Result<std::shared_ptr<Array>> Cast(const Array& value, std::shared_ptr<DataType> to_type, - const CastOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, Cast(Datum(value), to_type, options, ctx)); - return result.make_array(); -} - -Result<std::shared_ptr<CastFunction>> GetCastFunction( - const std::shared_ptr<DataType>& to_type) { - return internal::GetCastFunctionInternal(to_type); -} - -bool CanCast(const DataType& from_type, const DataType& to_type) { - internal::EnsureInitCastTable(); +} + +Result<Datum> Cast(const Datum& value, const CastOptions& options, ExecContext* ctx) { + return CallFunction("cast", {value}, &options, ctx); +} + +Result<Datum> Cast(const Datum& value, std::shared_ptr<DataType> to_type, + const CastOptions& options, ExecContext* ctx) { + CastOptions options_with_to_type = options; + options_with_to_type.to_type = to_type; + return Cast(value, options_with_to_type, ctx); +} + +Result<std::shared_ptr<Array>> Cast(const Array& value, std::shared_ptr<DataType> to_type, + const CastOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, Cast(Datum(value), to_type, options, ctx)); + return result.make_array(); +} + +Result<std::shared_ptr<CastFunction>> GetCastFunction( + const std::shared_ptr<DataType>& to_type) { + return internal::GetCastFunctionInternal(to_type); +} + +bool CanCast(const DataType& from_type, const DataType& to_type) { + internal::EnsureInitCastTable(); auto it = internal::g_cast_table.find(static_cast<int>(to_type.id())); - if (it == internal::g_cast_table.end()) { - return false; - } + if (it == internal::g_cast_table.end()) { + return false; + } const CastFunction* function = it->second.get(); DCHECK_EQ(function->out_type_id(), to_type.id()); @@ -251,8 +251,8 @@ bool CanCast(const DataType& from_type, const DataType& to_type) { } return false; -} - +} + Result<std::vector<Datum>> Cast(std::vector<Datum> datums, std::vector<ValueDescr> descrs, ExecContext* ctx) { for (size_t i = 0; i != datums.size(); ++i) { @@ -269,5 +269,5 @@ Result<std::vector<Datum>> Cast(std::vector<Datum> datums, std::vector<ValueDesc return datums; } -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.h index a0944ac721..131f57f892 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast.h @@ -1,156 +1,156 @@ -// 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 <memory> -#include <string> -#include <vector> - -#include "arrow/compute/function.h" -#include "arrow/compute/kernel.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { - -class Array; - -namespace compute { - -class ExecContext; - -/// \addtogroup compute-concrete-options -/// @{ - +// 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 <memory> +#include <string> +#include <vector> + +#include "arrow/compute/function.h" +#include "arrow/compute/kernel.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { + +class Array; + +namespace compute { + +class ExecContext; + +/// \addtogroup compute-concrete-options +/// @{ + class ARROW_EXPORT CastOptions : public FunctionOptions { public: explicit CastOptions(bool safe = true); - + constexpr static char const kTypeName[] = "CastOptions"; static CastOptions Safe(std::shared_ptr<DataType> to_type = NULLPTR) { CastOptions safe(true); safe.to_type = std::move(to_type); return safe; } - + static CastOptions Unsafe(std::shared_ptr<DataType> to_type = NULLPTR) { CastOptions unsafe(false); unsafe.to_type = std::move(to_type); return unsafe; } - - // Type being casted to. May be passed separate to eager function - // compute::Cast - std::shared_ptr<DataType> to_type; - - bool allow_int_overflow; - bool allow_time_truncate; - bool allow_time_overflow; - bool allow_decimal_truncate; - bool allow_float_truncate; - // Indicate if conversions from Binary/FixedSizeBinary to string must - // validate the utf8 payload. - bool allow_invalid_utf8; -}; - -/// @} - -// Cast functions are _not_ registered in the FunctionRegistry, though they use -// the same execution machinery -class CastFunction : public ScalarFunction { - public: + + // Type being casted to. May be passed separate to eager function + // compute::Cast + std::shared_ptr<DataType> to_type; + + bool allow_int_overflow; + bool allow_time_truncate; + bool allow_time_overflow; + bool allow_decimal_truncate; + bool allow_float_truncate; + // Indicate if conversions from Binary/FixedSizeBinary to string must + // validate the utf8 payload. + bool allow_invalid_utf8; +}; + +/// @} + +// Cast functions are _not_ registered in the FunctionRegistry, though they use +// the same execution machinery +class CastFunction : public ScalarFunction { + public: CastFunction(std::string name, Type::type out_type_id); - + Type::type out_type_id() const { return out_type_id_; } const std::vector<Type::type>& in_type_ids() const { return in_type_ids_; } - - Status AddKernel(Type::type in_type_id, std::vector<InputType> in_types, - OutputType out_type, ArrayKernelExec exec, - NullHandling::type = NullHandling::INTERSECTION, - MemAllocation::type = MemAllocation::PREALLOCATE); - - // Note, this function toggles off memory allocation and sets the init - // function to CastInit - Status AddKernel(Type::type in_type_id, ScalarKernel kernel); - + + Status AddKernel(Type::type in_type_id, std::vector<InputType> in_types, + OutputType out_type, ArrayKernelExec exec, + NullHandling::type = NullHandling::INTERSECTION, + MemAllocation::type = MemAllocation::PREALLOCATE); + + // Note, this function toggles off memory allocation and sets the init + // function to CastInit + Status AddKernel(Type::type in_type_id, ScalarKernel kernel); + Result<const Kernel*> DispatchExact( - const std::vector<ValueDescr>& values) const override; - - private: + const std::vector<ValueDescr>& values) const override; + + private: std::vector<Type::type> in_type_ids_; const Type::type out_type_id_; -}; - -ARROW_EXPORT -Result<std::shared_ptr<CastFunction>> GetCastFunction( - const std::shared_ptr<DataType>& to_type); - -/// \brief Return true if a cast function is defined -ARROW_EXPORT -bool CanCast(const DataType& from_type, const DataType& to_type); - -// ---------------------------------------------------------------------- -// Convenience invocation APIs for a number of kernels - -/// \brief Cast from one array type to another -/// \param[in] value array to cast -/// \param[in] to_type type to cast to -/// \param[in] options casting options -/// \param[in] ctx the function execution context, optional -/// \return the resulting array -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<std::shared_ptr<Array>> Cast(const Array& value, std::shared_ptr<DataType> to_type, - const CastOptions& options = CastOptions::Safe(), - ExecContext* ctx = NULLPTR); - -/// \brief Cast from one array type to another -/// \param[in] value array to cast -/// \param[in] options casting options. The "to_type" field must be populated -/// \param[in] ctx the function execution context, optional -/// \return the resulting array -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Cast(const Datum& value, const CastOptions& options, - ExecContext* ctx = NULLPTR); - -/// \brief Cast from one value to another -/// \param[in] value datum to cast -/// \param[in] to_type type to cast to -/// \param[in] options casting options -/// \param[in] ctx the function execution context, optional -/// \return the resulting datum -/// -/// \since 1.0.0 -/// \note API not yet finalized -ARROW_EXPORT -Result<Datum> Cast(const Datum& value, std::shared_ptr<DataType> to_type, - const CastOptions& options = CastOptions::Safe(), - ExecContext* ctx = NULLPTR); - +}; + +ARROW_EXPORT +Result<std::shared_ptr<CastFunction>> GetCastFunction( + const std::shared_ptr<DataType>& to_type); + +/// \brief Return true if a cast function is defined +ARROW_EXPORT +bool CanCast(const DataType& from_type, const DataType& to_type); + +// ---------------------------------------------------------------------- +// Convenience invocation APIs for a number of kernels + +/// \brief Cast from one array type to another +/// \param[in] value array to cast +/// \param[in] to_type type to cast to +/// \param[in] options casting options +/// \param[in] ctx the function execution context, optional +/// \return the resulting array +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<std::shared_ptr<Array>> Cast(const Array& value, std::shared_ptr<DataType> to_type, + const CastOptions& options = CastOptions::Safe(), + ExecContext* ctx = NULLPTR); + +/// \brief Cast from one array type to another +/// \param[in] value array to cast +/// \param[in] options casting options. The "to_type" field must be populated +/// \param[in] ctx the function execution context, optional +/// \return the resulting array +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Cast(const Datum& value, const CastOptions& options, + ExecContext* ctx = NULLPTR); + +/// \brief Cast from one value to another +/// \param[in] value datum to cast +/// \param[in] to_type type to cast to +/// \param[in] options casting options +/// \param[in] ctx the function execution context, optional +/// \return the resulting datum +/// +/// \since 1.0.0 +/// \note API not yet finalized +ARROW_EXPORT +Result<Datum> Cast(const Datum& value, std::shared_ptr<DataType> to_type, + const CastOptions& options = CastOptions::Safe(), + ExecContext* ctx = NULLPTR); + /// \brief Cast several values simultaneously. Safe cast options are used. /// \param[in] values datums to cast /// \param[in] descrs ValueDescrs to cast to @@ -163,5 +163,5 @@ ARROW_EXPORT Result<std::vector<Datum>> Cast(std::vector<Datum> values, std::vector<ValueDescr> descrs, ExecContext* ctx = NULLPTR); -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast_internal.h index 97975b8006..0105d08a57 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/cast_internal.h @@ -1,43 +1,43 @@ -// 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 <memory> -#include <vector> - -#include "arrow/compute/cast.h" // IWYU pragma: keep -#include "arrow/compute/kernel.h" // IWYU pragma: keep -#include "arrow/compute/kernels/codegen_internal.h" // IWYU pragma: keep - -namespace arrow { -namespace compute { -namespace internal { - -using CastState = OptionsWrapper<CastOptions>; - -// See kernels/scalar_cast_*.cc for these -std::vector<std::shared_ptr<CastFunction>> GetBooleanCasts(); -std::vector<std::shared_ptr<CastFunction>> GetNumericCasts(); -std::vector<std::shared_ptr<CastFunction>> GetTemporalCasts(); -std::vector<std::shared_ptr<CastFunction>> GetBinaryLikeCasts(); -std::vector<std::shared_ptr<CastFunction>> GetNestedCasts(); +// 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 <memory> +#include <vector> + +#include "arrow/compute/cast.h" // IWYU pragma: keep +#include "arrow/compute/kernel.h" // IWYU pragma: keep +#include "arrow/compute/kernels/codegen_internal.h" // IWYU pragma: keep + +namespace arrow { +namespace compute { +namespace internal { + +using CastState = OptionsWrapper<CastOptions>; + +// See kernels/scalar_cast_*.cc for these +std::vector<std::shared_ptr<CastFunction>> GetBooleanCasts(); +std::vector<std::shared_ptr<CastFunction>> GetNumericCasts(); +std::vector<std::shared_ptr<CastFunction>> GetTemporalCasts(); +std::vector<std::shared_ptr<CastFunction>> GetBinaryLikeCasts(); +std::vector<std::shared_ptr<CastFunction>> GetNestedCasts(); std::vector<std::shared_ptr<CastFunction>> GetDictionaryCasts(); - -} // namespace internal -} // namespace compute -} // namespace arrow + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.cc index 55fb256a36..63f8d39f55 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.cc @@ -1,64 +1,64 @@ -// 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. - -#include "arrow/compute/exec.h" - -#include <algorithm> -#include <cstddef> -#include <cstdint> -#include <memory> -#include <utility> -#include <vector> - -#include "arrow/array/array_base.h" -#include "arrow/array/array_primitive.h" -#include "arrow/array/data.h" -#include "arrow/array/util.h" -#include "arrow/buffer.h" -#include "arrow/chunked_array.h" -#include "arrow/compute/exec_internal.h" -#include "arrow/compute/function.h" -#include "arrow/compute/kernel.h" -#include "arrow/compute/registry.h" -#include "arrow/compute/util_internal.h" -#include "arrow/datum.h" +// 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. + +#include "arrow/compute/exec.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <utility> +#include <vector> + +#include "arrow/array/array_base.h" +#include "arrow/array/array_primitive.h" +#include "arrow/array/data.h" +#include "arrow/array/util.h" +#include "arrow/buffer.h" +#include "arrow/chunked_array.h" +#include "arrow/compute/exec_internal.h" +#include "arrow/compute/function.h" +#include "arrow/compute/kernel.h" +#include "arrow/compute/registry.h" +#include "arrow/compute/util_internal.h" +#include "arrow/datum.h" #include "arrow/pretty_print.h" #include "arrow/record_batch.h" -#include "arrow/scalar.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_traits.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_ops.h" -#include "arrow/util/checked_cast.h" -#include "arrow/util/cpu_info.h" -#include "arrow/util/logging.h" +#include "arrow/scalar.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_traits.h" +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_ops.h" +#include "arrow/util/checked_cast.h" +#include "arrow/util/cpu_info.h" +#include "arrow/util/logging.h" #include "arrow/util/make_unique.h" #include "arrow/util/vector.h" - -namespace arrow { - -using internal::BitmapAnd; -using internal::checked_cast; -using internal::CopyBitmap; -using internal::CpuInfo; - -namespace compute { - + +namespace arrow { + +using internal::BitmapAnd; +using internal::checked_cast; +using internal::CopyBitmap; +using internal::CpuInfo; + +namespace compute { + ExecContext* default_exec_context() { static ExecContext default_ctx; return &default_ctx; @@ -157,22 +157,22 @@ Result<std::shared_ptr<RecordBatch>> ExecBatch::ToRecordBatch( return RecordBatch::Make(std::move(schema), length, std::move(columns)); } -namespace { - -Result<std::shared_ptr<Buffer>> AllocateDataBuffer(KernelContext* ctx, int64_t length, - int bit_width) { - if (bit_width == 1) { - return ctx->AllocateBitmap(length); - } else { +namespace { + +Result<std::shared_ptr<Buffer>> AllocateDataBuffer(KernelContext* ctx, int64_t length, + int bit_width) { + if (bit_width == 1) { + return ctx->AllocateBitmap(length); + } else { int64_t buffer_size = BitUtil::BytesForBits(length * bit_width); - return ctx->Allocate(buffer_size); - } -} - + return ctx->Allocate(buffer_size); + } +} + struct BufferPreallocation { explicit BufferPreallocation(int bit_width = -1, int added_length = 0) : bit_width(bit_width), added_length(added_length) {} - + int bit_width; int added_length; }; @@ -182,7 +182,7 @@ void ComputeDataPreallocate(const DataType& type, if (is_fixed_width(type.id()) && type.id() != Type::NA) { widths->emplace_back(checked_cast<const FixedWidthType&>(type).bit_width()); return; - } + } // Preallocate binary and list offsets switch (type.id()) { case Type::BINARY: @@ -199,12 +199,12 @@ void ComputeDataPreallocate(const DataType& type, default: break; } -} - -} // namespace - -namespace detail { - +} + +} // namespace + +namespace detail { + Status CheckAllValues(const std::vector<Datum>& values) { for (const auto& value : values) { if (!value.is_value()) { @@ -215,102 +215,102 @@ Status CheckAllValues(const std::vector<Datum>& values) { return Status::OK(); } -ExecBatchIterator::ExecBatchIterator(std::vector<Datum> args, int64_t length, - int64_t max_chunksize) - : args_(std::move(args)), - position_(0), - length_(length), - max_chunksize_(max_chunksize) { - chunk_indexes_.resize(args_.size(), 0); - chunk_positions_.resize(args_.size(), 0); -} - -Result<std::unique_ptr<ExecBatchIterator>> ExecBatchIterator::Make( - std::vector<Datum> args, int64_t max_chunksize) { - for (const auto& arg : args) { - if (!(arg.is_arraylike() || arg.is_scalar())) { - return Status::Invalid( - "ExecBatchIterator only works with Scalar, Array, and " - "ChunkedArray arguments"); - } - } - - // If the arguments are all scalars, then the length is 1 - int64_t length = 1; - - bool length_set = false; - for (auto& arg : args) { - if (arg.is_scalar()) { - continue; - } - if (!length_set) { - length = arg.length(); - length_set = true; - } else { - if (arg.length() != length) { - return Status::Invalid("Array arguments must all be the same length"); - } - } - } - - max_chunksize = std::min(length, max_chunksize); - - return std::unique_ptr<ExecBatchIterator>( - new ExecBatchIterator(std::move(args), length, max_chunksize)); -} - -bool ExecBatchIterator::Next(ExecBatch* batch) { - if (position_ == length_) { - return false; - } - - // Determine how large the common contiguous "slice" of all the arguments is - int64_t iteration_size = std::min(length_ - position_, max_chunksize_); - - // If length_ is 0, then this loop will never execute - for (size_t i = 0; i < args_.size() && iteration_size > 0; ++i) { - // If the argument is not a chunked array, it's either a Scalar or Array, - // in which case it doesn't influence the size of this batch. Note that if - // the args are all scalars the batch length is 1 - if (args_[i].kind() != Datum::CHUNKED_ARRAY) { - continue; - } - const ChunkedArray& arg = *args_[i].chunked_array(); - std::shared_ptr<Array> current_chunk; - while (true) { - current_chunk = arg.chunk(chunk_indexes_[i]); - if (chunk_positions_[i] == current_chunk->length()) { - // Chunk is zero-length, or was exhausted in the previous iteration - chunk_positions_[i] = 0; - ++chunk_indexes_[i]; - continue; - } - break; - } - iteration_size = - std::min(current_chunk->length() - chunk_positions_[i], iteration_size); - } - - // Now, fill the batch - batch->values.resize(args_.size()); - batch->length = iteration_size; - for (size_t i = 0; i < args_.size(); ++i) { - if (args_[i].is_scalar()) { - batch->values[i] = args_[i].scalar(); - } else if (args_[i].is_array()) { - batch->values[i] = args_[i].array()->Slice(position_, iteration_size); - } else { - const ChunkedArray& carr = *args_[i].chunked_array(); - const auto& chunk = carr.chunk(chunk_indexes_[i]); - batch->values[i] = chunk->data()->Slice(chunk_positions_[i], iteration_size); - chunk_positions_[i] += iteration_size; - } - } - position_ += iteration_size; - DCHECK_LE(position_, length_); - return true; -} - +ExecBatchIterator::ExecBatchIterator(std::vector<Datum> args, int64_t length, + int64_t max_chunksize) + : args_(std::move(args)), + position_(0), + length_(length), + max_chunksize_(max_chunksize) { + chunk_indexes_.resize(args_.size(), 0); + chunk_positions_.resize(args_.size(), 0); +} + +Result<std::unique_ptr<ExecBatchIterator>> ExecBatchIterator::Make( + std::vector<Datum> args, int64_t max_chunksize) { + for (const auto& arg : args) { + if (!(arg.is_arraylike() || arg.is_scalar())) { + return Status::Invalid( + "ExecBatchIterator only works with Scalar, Array, and " + "ChunkedArray arguments"); + } + } + + // If the arguments are all scalars, then the length is 1 + int64_t length = 1; + + bool length_set = false; + for (auto& arg : args) { + if (arg.is_scalar()) { + continue; + } + if (!length_set) { + length = arg.length(); + length_set = true; + } else { + if (arg.length() != length) { + return Status::Invalid("Array arguments must all be the same length"); + } + } + } + + max_chunksize = std::min(length, max_chunksize); + + return std::unique_ptr<ExecBatchIterator>( + new ExecBatchIterator(std::move(args), length, max_chunksize)); +} + +bool ExecBatchIterator::Next(ExecBatch* batch) { + if (position_ == length_) { + return false; + } + + // Determine how large the common contiguous "slice" of all the arguments is + int64_t iteration_size = std::min(length_ - position_, max_chunksize_); + + // If length_ is 0, then this loop will never execute + for (size_t i = 0; i < args_.size() && iteration_size > 0; ++i) { + // If the argument is not a chunked array, it's either a Scalar or Array, + // in which case it doesn't influence the size of this batch. Note that if + // the args are all scalars the batch length is 1 + if (args_[i].kind() != Datum::CHUNKED_ARRAY) { + continue; + } + const ChunkedArray& arg = *args_[i].chunked_array(); + std::shared_ptr<Array> current_chunk; + while (true) { + current_chunk = arg.chunk(chunk_indexes_[i]); + if (chunk_positions_[i] == current_chunk->length()) { + // Chunk is zero-length, or was exhausted in the previous iteration + chunk_positions_[i] = 0; + ++chunk_indexes_[i]; + continue; + } + break; + } + iteration_size = + std::min(current_chunk->length() - chunk_positions_[i], iteration_size); + } + + // Now, fill the batch + batch->values.resize(args_.size()); + batch->length = iteration_size; + for (size_t i = 0; i < args_.size(); ++i) { + if (args_[i].is_scalar()) { + batch->values[i] = args_[i].scalar(); + } else if (args_[i].is_array()) { + batch->values[i] = args_[i].array()->Slice(position_, iteration_size); + } else { + const ChunkedArray& carr = *args_[i].chunked_array(); + const auto& chunk = carr.chunk(chunk_indexes_[i]); + batch->values[i] = chunk->data()->Slice(chunk_positions_[i], iteration_size); + chunk_positions_[i] += iteration_size; + } + } + position_ += iteration_size; + DCHECK_LE(position_, length_); + return true; +} + namespace { struct NullGeneralization { @@ -327,7 +327,7 @@ struct NullGeneralization { const auto& arr = *datum.array(); - // Do not count the bits if they haven't been counted already + // Do not count the bits if they haven't been counted already const int64_t known_null_count = arr.null_count.load(); if ((known_null_count == 0) || (arr.buffers[0] == NULLPTR)) { return ALL_VALID; @@ -338,88 +338,88 @@ struct NullGeneralization { } return PERHAPS_NULL; - } + } }; - -// Null propagation implementation that deals both with preallocated bitmaps -// and maybe-to-be allocated bitmaps -// -// If the bitmap is preallocated, it MUST be populated (since it might be a -// view of a much larger bitmap). If it isn't preallocated, then we have -// more flexibility. -// -// * If the batch has no nulls, then we do nothing -// * If only a single array has nulls, and its offset is a multiple of 8, -// then we can zero-copy the bitmap into the output -// * Otherwise, we allocate the bitmap and populate it -class NullPropagator { - public: - NullPropagator(KernelContext* ctx, const ExecBatch& batch, ArrayData* output) - : ctx_(ctx), batch_(batch), output_(output) { + +// Null propagation implementation that deals both with preallocated bitmaps +// and maybe-to-be allocated bitmaps +// +// If the bitmap is preallocated, it MUST be populated (since it might be a +// view of a much larger bitmap). If it isn't preallocated, then we have +// more flexibility. +// +// * If the batch has no nulls, then we do nothing +// * If only a single array has nulls, and its offset is a multiple of 8, +// then we can zero-copy the bitmap into the output +// * Otherwise, we allocate the bitmap and populate it +class NullPropagator { + public: + NullPropagator(KernelContext* ctx, const ExecBatch& batch, ArrayData* output) + : ctx_(ctx), batch_(batch), output_(output) { for (const Datum& datum : batch_.values) { auto null_generalization = NullGeneralization::Get(datum); if (null_generalization == NullGeneralization::ALL_NULL) { is_all_null_ = true; - } + } if (null_generalization != NullGeneralization::ALL_VALID && datum.kind() == Datum::ARRAY) { arrays_with_nulls_.push_back(datum.array().get()); } - } - - if (output->buffers[0] != nullptr) { - bitmap_preallocated_ = true; - SetBitmap(output_->buffers[0].get()); - } - } - - void SetBitmap(Buffer* bitmap) { bitmap_ = bitmap->mutable_data(); } - - Status EnsureAllocated() { - if (bitmap_preallocated_) { - return Status::OK(); - } - ARROW_ASSIGN_OR_RAISE(output_->buffers[0], ctx_->AllocateBitmap(output_->length)); - SetBitmap(output_->buffers[0].get()); - return Status::OK(); - } - + } + + if (output->buffers[0] != nullptr) { + bitmap_preallocated_ = true; + SetBitmap(output_->buffers[0].get()); + } + } + + void SetBitmap(Buffer* bitmap) { bitmap_ = bitmap->mutable_data(); } + + Status EnsureAllocated() { + if (bitmap_preallocated_) { + return Status::OK(); + } + ARROW_ASSIGN_OR_RAISE(output_->buffers[0], ctx_->AllocateBitmap(output_->length)); + SetBitmap(output_->buffers[0].get()); + return Status::OK(); + } + Status AllNullShortCircuit() { // OK, the output should be all null output_->null_count = output_->length; - + if (bitmap_preallocated_) { BitUtil::SetBitsTo(bitmap_, output_->offset, output_->length, false); return Status::OK(); } - // Walk all the values with nulls instead of breaking on the first in case - // we find a bitmap that can be reused in the non-preallocated case + // Walk all the values with nulls instead of breaking on the first in case + // we find a bitmap that can be reused in the non-preallocated case for (const ArrayData* arr : arrays_with_nulls_) { if (arr->null_count.load() == arr->length && arr->buffers[0] != nullptr) { // Reuse this all null bitmap output_->buffers[0] = arr->buffers[0]; return Status::OK(); - } - } - + } + } + RETURN_NOT_OK(EnsureAllocated()); BitUtil::SetBitsTo(bitmap_, output_->offset, output_->length, false); return Status::OK(); - } - - Status PropagateSingle() { - // One array + } + + Status PropagateSingle() { + // One array const ArrayData& arr = *arrays_with_nulls_[0]; - const std::shared_ptr<Buffer>& arr_bitmap = arr.buffers[0]; - - // Reuse the null count if it's known - output_->null_count = arr.null_count.load(); - - if (bitmap_preallocated_) { - CopyBitmap(arr_bitmap->data(), arr.offset, arr.length, bitmap_, output_->offset); + const std::shared_ptr<Buffer>& arr_bitmap = arr.buffers[0]; + + // Reuse the null count if it's known + output_->null_count = arr.null_count.load(); + + if (bitmap_preallocated_) { + CopyBitmap(arr_bitmap->data(), arr.offset, arr.length, bitmap_, output_->offset); return Status::OK(); } @@ -437,144 +437,144 @@ class NullPropagator { } else if (arr.offset % 8 == 0) { output_->buffers[0] = SliceBuffer(arr_bitmap, arr.offset / 8, BitUtil::BytesForBits(arr.length)); - } else { + } else { RETURN_NOT_OK(EnsureAllocated()); CopyBitmap(arr_bitmap->data(), arr.offset, arr.length, bitmap_, /*dst_offset=*/0); - } - return Status::OK(); - } - - Status PropagateMultiple() { - // More than one array. We use BitmapAnd to intersect their bitmaps - - // Do not compute the intersection null count until it's needed - RETURN_NOT_OK(EnsureAllocated()); - - auto Accumulate = [&](const ArrayData& left, const ArrayData& right) { - DCHECK(left.buffers[0]); - DCHECK(right.buffers[0]); - BitmapAnd(left.buffers[0]->data(), left.offset, right.buffers[0]->data(), - right.offset, output_->length, output_->offset, - output_->buffers[0]->mutable_data()); - }; - + } + return Status::OK(); + } + + Status PropagateMultiple() { + // More than one array. We use BitmapAnd to intersect their bitmaps + + // Do not compute the intersection null count until it's needed + RETURN_NOT_OK(EnsureAllocated()); + + auto Accumulate = [&](const ArrayData& left, const ArrayData& right) { + DCHECK(left.buffers[0]); + DCHECK(right.buffers[0]); + BitmapAnd(left.buffers[0]->data(), left.offset, right.buffers[0]->data(), + right.offset, output_->length, output_->offset, + output_->buffers[0]->mutable_data()); + }; + DCHECK_GT(arrays_with_nulls_.size(), 1); - - // Seed the output bitmap with the & of the first two bitmaps + + // Seed the output bitmap with the & of the first two bitmaps Accumulate(*arrays_with_nulls_[0], *arrays_with_nulls_[1]); - - // Accumulate the rest + + // Accumulate the rest for (size_t i = 2; i < arrays_with_nulls_.size(); ++i) { Accumulate(*output_, *arrays_with_nulls_[i]); - } - return Status::OK(); - } - - Status Execute() { + } + return Status::OK(); + } + + Status Execute() { if (is_all_null_) { // An all-null value (scalar null or all-null array) gives us a short // circuit opportunity return AllNullShortCircuit(); - } - - // At this point, by construction we know that all of the values in + } + + // At this point, by construction we know that all of the values in // arrays_with_nulls_ are arrays that are not all null. So there are a - // few cases: - // - // * No arrays. This is a no-op w/o preallocation but when the bitmap is - // pre-allocated we have to fill it with 1's - // * One array, whose bitmap can be zero-copied (w/o preallocation, and - // when no byte is split) or copied (split byte or w/ preallocation) - // * More than one array, we must compute the intersection of all the - // bitmaps - // - // BUT, if the output offset is nonzero for some reason, we copy into the - // output unconditionally - - output_->null_count = kUnknownNullCount; - + // few cases: + // + // * No arrays. This is a no-op w/o preallocation but when the bitmap is + // pre-allocated we have to fill it with 1's + // * One array, whose bitmap can be zero-copied (w/o preallocation, and + // when no byte is split) or copied (split byte or w/ preallocation) + // * More than one array, we must compute the intersection of all the + // bitmaps + // + // BUT, if the output offset is nonzero for some reason, we copy into the + // output unconditionally + + output_->null_count = kUnknownNullCount; + if (arrays_with_nulls_.empty()) { - // No arrays with nulls case - output_->null_count = 0; - if (bitmap_preallocated_) { - BitUtil::SetBitsTo(bitmap_, output_->offset, output_->length, true); - } - return Status::OK(); + // No arrays with nulls case + output_->null_count = 0; + if (bitmap_preallocated_) { + BitUtil::SetBitsTo(bitmap_, output_->offset, output_->length, true); + } + return Status::OK(); } if (arrays_with_nulls_.size() == 1) { - return PropagateSingle(); - } + return PropagateSingle(); + } return PropagateMultiple(); - } - - private: - KernelContext* ctx_; - const ExecBatch& batch_; + } + + private: + KernelContext* ctx_; + const ExecBatch& batch_; std::vector<const ArrayData*> arrays_with_nulls_; bool is_all_null_ = false; - ArrayData* output_; - uint8_t* bitmap_; - bool bitmap_preallocated_ = false; -}; - -std::shared_ptr<ChunkedArray> ToChunkedArray(const std::vector<Datum>& values, - const std::shared_ptr<DataType>& type) { - std::vector<std::shared_ptr<Array>> arrays; + ArrayData* output_; + uint8_t* bitmap_; + bool bitmap_preallocated_ = false; +}; + +std::shared_ptr<ChunkedArray> ToChunkedArray(const std::vector<Datum>& values, + const std::shared_ptr<DataType>& type) { + std::vector<std::shared_ptr<Array>> arrays; arrays.reserve(values.size()); for (const Datum& val : values) { if (val.length() == 0) { - // Skip empty chunks - continue; - } + // Skip empty chunks + continue; + } arrays.emplace_back(val.make_array()); - } + } return std::make_shared<ChunkedArray>(std::move(arrays), type); -} - -bool HaveChunkedArray(const std::vector<Datum>& values) { - for (const auto& value : values) { - if (value.kind() == Datum::CHUNKED_ARRAY) { - return true; - } - } - return false; -} - +} + +bool HaveChunkedArray(const std::vector<Datum>& values) { + for (const auto& value : values) { + if (value.kind() == Datum::CHUNKED_ARRAY) { + return true; + } + } + return false; +} + template <typename KernelType> class KernelExecutorImpl : public KernelExecutor { - public: + public: Status Init(KernelContext* kernel_ctx, KernelInitArgs args) override { kernel_ctx_ = kernel_ctx; kernel_ = static_cast<const KernelType*>(args.kernel); - + // Resolve the output descriptor for this kernel ARROW_ASSIGN_OR_RAISE( output_descr_, kernel_->signature->out_type().Resolve(kernel_ctx_, args.inputs)); - - return Status::OK(); - } - + + return Status::OK(); + } + protected: - // This is overridden by the VectorExecutor - virtual Status SetupArgIteration(const std::vector<Datum>& args) { + // This is overridden by the VectorExecutor + virtual Status SetupArgIteration(const std::vector<Datum>& args) { ARROW_ASSIGN_OR_RAISE( batch_iterator_, ExecBatchIterator::Make(args, exec_context()->exec_chunksize())); - return Status::OK(); - } - - Result<std::shared_ptr<ArrayData>> PrepareOutput(int64_t length) { - auto out = std::make_shared<ArrayData>(output_descr_.type, length); - out->buffers.resize(output_num_buffers_); - - if (validity_preallocated_) { + return Status::OK(); + } + + Result<std::shared_ptr<ArrayData>> PrepareOutput(int64_t length) { + auto out = std::make_shared<ArrayData>(output_descr_.type, length); + out->buffers.resize(output_num_buffers_); + + if (validity_preallocated_) { ARROW_ASSIGN_OR_RAISE(out->buffers[0], kernel_ctx_->AllocateBitmap(length)); - } + } if (kernel_->null_handling == NullHandling::OUTPUT_NOT_NULL) { out->null_count = 0; - } + } for (size_t i = 0; i < data_preallocated_.size(); ++i) { const auto& prealloc = data_preallocated_[i]; if (prealloc.bit_width >= 0) { @@ -584,183 +584,183 @@ class KernelExecutorImpl : public KernelExecutor { prealloc.bit_width)); } } - return out; - } - + return out; + } + ExecContext* exec_context() { return kernel_ctx_->exec_context(); } KernelState* state() { return kernel_ctx_->state(); } - - // Not all of these members are used for every executor type - + + // Not all of these members are used for every executor type + KernelContext* kernel_ctx_; - const KernelType* kernel_; - std::unique_ptr<ExecBatchIterator> batch_iterator_; - ValueDescr output_descr_; - - int output_num_buffers_; - - // If true, then memory is preallocated for the validity bitmap with the same - // strategy as the data buffer(s). - bool validity_preallocated_ = false; + const KernelType* kernel_; + std::unique_ptr<ExecBatchIterator> batch_iterator_; + ValueDescr output_descr_; + + int output_num_buffers_; + + // If true, then memory is preallocated for the validity bitmap with the same + // strategy as the data buffer(s). + bool validity_preallocated_ = false; // The kernel writes into data buffers preallocated for these bit widths // (0 indicates no preallocation); std::vector<BufferPreallocation> data_preallocated_; -}; - +}; + class ScalarExecutor : public KernelExecutorImpl<ScalarKernel> { - public: - Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { - RETURN_NOT_OK(PrepareExecute(args)); - ExecBatch batch; - while (batch_iterator_->Next(&batch)) { - RETURN_NOT_OK(ExecuteBatch(batch, listener)); - } - if (preallocate_contiguous_) { - // If we preallocated one big chunk, since the kernel execution is - // completed, we can now emit it - RETURN_NOT_OK(listener->OnResult(std::move(preallocated_))); - } - return Status::OK(); - } - - Datum WrapResults(const std::vector<Datum>& inputs, - const std::vector<Datum>& outputs) override { - if (output_descr_.shape == ValueDescr::SCALAR) { - DCHECK_GT(outputs.size(), 0); - if (outputs.size() == 1) { - // Return as SCALAR - return outputs[0]; - } else { - // Return as COLLECTION - return outputs; - } - } else { - // If execution yielded multiple chunks (because large arrays were split - // based on the ExecContext parameters, then the result is a ChunkedArray - if (HaveChunkedArray(inputs) || outputs.size() > 1) { - return ToChunkedArray(outputs, output_descr_.type); - } else if (outputs.size() == 1) { - // Outputs have just one element - return outputs[0]; - } else { - // XXX: In the case where no outputs are omitted, is returning a 0-length - // array always the correct move? + public: + Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { + RETURN_NOT_OK(PrepareExecute(args)); + ExecBatch batch; + while (batch_iterator_->Next(&batch)) { + RETURN_NOT_OK(ExecuteBatch(batch, listener)); + } + if (preallocate_contiguous_) { + // If we preallocated one big chunk, since the kernel execution is + // completed, we can now emit it + RETURN_NOT_OK(listener->OnResult(std::move(preallocated_))); + } + return Status::OK(); + } + + Datum WrapResults(const std::vector<Datum>& inputs, + const std::vector<Datum>& outputs) override { + if (output_descr_.shape == ValueDescr::SCALAR) { + DCHECK_GT(outputs.size(), 0); + if (outputs.size() == 1) { + // Return as SCALAR + return outputs[0]; + } else { + // Return as COLLECTION + return outputs; + } + } else { + // If execution yielded multiple chunks (because large arrays were split + // based on the ExecContext parameters, then the result is a ChunkedArray + if (HaveChunkedArray(inputs) || outputs.size() > 1) { + return ToChunkedArray(outputs, output_descr_.type); + } else if (outputs.size() == 1) { + // Outputs have just one element + return outputs[0]; + } else { + // XXX: In the case where no outputs are omitted, is returning a 0-length + // array always the correct move? return MakeArrayOfNull(output_descr_.type, /*length=*/0, exec_context()->memory_pool()) .ValueOrDie(); - } - } - } - - protected: - Status ExecuteBatch(const ExecBatch& batch, ExecListener* listener) { - Datum out; - RETURN_NOT_OK(PrepareNextOutput(batch, &out)); - - if (output_descr_.shape == ValueDescr::ARRAY) { - ArrayData* out_arr = out.mutable_array(); - if (kernel_->null_handling == NullHandling::INTERSECTION) { + } + } + } + + protected: + Status ExecuteBatch(const ExecBatch& batch, ExecListener* listener) { + Datum out; + RETURN_NOT_OK(PrepareNextOutput(batch, &out)); + + if (output_descr_.shape == ValueDescr::ARRAY) { + ArrayData* out_arr = out.mutable_array(); + if (kernel_->null_handling == NullHandling::INTERSECTION) { RETURN_NOT_OK(PropagateNulls(kernel_ctx_, batch, out_arr)); - } else if (kernel_->null_handling == NullHandling::OUTPUT_NOT_NULL) { - out_arr->null_count = 0; - } - } else { - if (kernel_->null_handling == NullHandling::INTERSECTION) { - // set scalar validity - out.scalar()->is_valid = - std::all_of(batch.values.begin(), batch.values.end(), - [](const Datum& input) { return input.scalar()->is_valid; }); - } else if (kernel_->null_handling == NullHandling::OUTPUT_NOT_NULL) { - out.scalar()->is_valid = true; - } - } - + } else if (kernel_->null_handling == NullHandling::OUTPUT_NOT_NULL) { + out_arr->null_count = 0; + } + } else { + if (kernel_->null_handling == NullHandling::INTERSECTION) { + // set scalar validity + out.scalar()->is_valid = + std::all_of(batch.values.begin(), batch.values.end(), + [](const Datum& input) { return input.scalar()->is_valid; }); + } else if (kernel_->null_handling == NullHandling::OUTPUT_NOT_NULL) { + out.scalar()->is_valid = true; + } + } + RETURN_NOT_OK(kernel_->exec(kernel_ctx_, batch, &out)); - if (!preallocate_contiguous_) { - // If we are producing chunked output rather than one big array, then - // emit each chunk as soon as it's available - RETURN_NOT_OK(listener->OnResult(std::move(out))); - } - return Status::OK(); - } - - Status PrepareExecute(const std::vector<Datum>& args) { + if (!preallocate_contiguous_) { + // If we are producing chunked output rather than one big array, then + // emit each chunk as soon as it's available + RETURN_NOT_OK(listener->OnResult(std::move(out))); + } + return Status::OK(); + } + + Status PrepareExecute(const std::vector<Datum>& args) { RETURN_NOT_OK(this->SetupArgIteration(args)); - - if (output_descr_.shape == ValueDescr::ARRAY) { - // If the executor is configured to produce a single large Array output for - // kernels supporting preallocation, then we do so up front and then - // iterate over slices of that large array. Otherwise, we preallocate prior - // to processing each batch emitted from the ExecBatchIterator - RETURN_NOT_OK(SetupPreallocation(batch_iterator_->length())); - } - return Status::OK(); - } - - // We must accommodate two different modes of execution for preallocated - // execution - // - // * A single large ("contiguous") allocation that we populate with results - // on a chunkwise basis according to the ExecBatchIterator. This permits - // parallelization even if the objective is to obtain a single Array or - // ChunkedArray at the end - // * A standalone buffer preallocation for each chunk emitted from the - // ExecBatchIterator - // - // When data buffer preallocation is not possible (e.g. with BINARY / STRING - // outputs), then contiguous results are only possible if the input is - // contiguous. - - Status PrepareNextOutput(const ExecBatch& batch, Datum* out) { - if (output_descr_.shape == ValueDescr::ARRAY) { - if (preallocate_contiguous_) { - // The output is already fully preallocated - const int64_t batch_start_position = batch_iterator_->position() - batch.length; - - if (batch.length < batch_iterator_->length()) { - // If this is a partial execution, then we write into a slice of - // preallocated_ - out->value = preallocated_->Slice(batch_start_position, batch.length); - } else { - // Otherwise write directly into preallocated_. The main difference - // computationally (versus the Slice approach) is that the null_count - // may not need to be recomputed in the result - out->value = preallocated_; - } - } else { - // We preallocate (maybe) only for the output of processing the current - // batch - ARROW_ASSIGN_OR_RAISE(out->value, PrepareOutput(batch.length)); - } - } else { - // For scalar outputs, we set a null scalar of the correct type to - // communicate the output type to the kernel if needed - // - // XXX: Is there some way to avoid this step? - out->value = MakeNullScalar(output_descr_.type); - } - return Status::OK(); - } - - Status SetupPreallocation(int64_t total_length) { - output_num_buffers_ = static_cast<int>(output_descr_.type->layout().buffers.size()); - - // Decide if we need to preallocate memory for this kernel - validity_preallocated_ = - (kernel_->null_handling != NullHandling::COMPUTED_NO_PREALLOCATE && + + if (output_descr_.shape == ValueDescr::ARRAY) { + // If the executor is configured to produce a single large Array output for + // kernels supporting preallocation, then we do so up front and then + // iterate over slices of that large array. Otherwise, we preallocate prior + // to processing each batch emitted from the ExecBatchIterator + RETURN_NOT_OK(SetupPreallocation(batch_iterator_->length())); + } + return Status::OK(); + } + + // We must accommodate two different modes of execution for preallocated + // execution + // + // * A single large ("contiguous") allocation that we populate with results + // on a chunkwise basis according to the ExecBatchIterator. This permits + // parallelization even if the objective is to obtain a single Array or + // ChunkedArray at the end + // * A standalone buffer preallocation for each chunk emitted from the + // ExecBatchIterator + // + // When data buffer preallocation is not possible (e.g. with BINARY / STRING + // outputs), then contiguous results are only possible if the input is + // contiguous. + + Status PrepareNextOutput(const ExecBatch& batch, Datum* out) { + if (output_descr_.shape == ValueDescr::ARRAY) { + if (preallocate_contiguous_) { + // The output is already fully preallocated + const int64_t batch_start_position = batch_iterator_->position() - batch.length; + + if (batch.length < batch_iterator_->length()) { + // If this is a partial execution, then we write into a slice of + // preallocated_ + out->value = preallocated_->Slice(batch_start_position, batch.length); + } else { + // Otherwise write directly into preallocated_. The main difference + // computationally (versus the Slice approach) is that the null_count + // may not need to be recomputed in the result + out->value = preallocated_; + } + } else { + // We preallocate (maybe) only for the output of processing the current + // batch + ARROW_ASSIGN_OR_RAISE(out->value, PrepareOutput(batch.length)); + } + } else { + // For scalar outputs, we set a null scalar of the correct type to + // communicate the output type to the kernel if needed + // + // XXX: Is there some way to avoid this step? + out->value = MakeNullScalar(output_descr_.type); + } + return Status::OK(); + } + + Status SetupPreallocation(int64_t total_length) { + output_num_buffers_ = static_cast<int>(output_descr_.type->layout().buffers.size()); + + // Decide if we need to preallocate memory for this kernel + validity_preallocated_ = + (kernel_->null_handling != NullHandling::COMPUTED_NO_PREALLOCATE && kernel_->null_handling != NullHandling::OUTPUT_NOT_NULL && output_descr_.type->id() != Type::NA); if (kernel_->mem_allocation == MemAllocation::PREALLOCATE) { ComputeDataPreallocate(*output_descr_.type, &data_preallocated_); } - + // Contiguous preallocation only possible on non-nested types if all // buffers are preallocated. Otherwise, we must go chunk-by-chunk. - // + // // Some kernels are also unable to write into sliced outputs, so we respect the // kernel's attributes. - preallocate_contiguous_ = + preallocate_contiguous_ = (exec_context()->preallocate_contiguous() && kernel_->can_write_into_slices && validity_preallocated_ && !is_nested(output_descr_.type->id()) && !is_dictionary(output_descr_.type->id()) && @@ -769,202 +769,202 @@ class ScalarExecutor : public KernelExecutorImpl<ScalarKernel> { [](const BufferPreallocation& prealloc) { return prealloc.bit_width >= 0; })); - if (preallocate_contiguous_) { - ARROW_ASSIGN_OR_RAISE(preallocated_, PrepareOutput(total_length)); - } - return Status::OK(); - } - - // If true, and the kernel and output type supports preallocation (for both - // the validity and data buffers), then we allocate one big array and then - // iterate through it while executing the kernel in chunks - bool preallocate_contiguous_ = false; - - // For storing a contiguous preallocation per above. Unused otherwise - std::shared_ptr<ArrayData> preallocated_; -}; - -Status PackBatchNoChunks(const std::vector<Datum>& args, ExecBatch* out) { - int64_t length = 0; - for (const auto& arg : args) { - switch (arg.kind()) { - case Datum::SCALAR: - case Datum::ARRAY: + if (preallocate_contiguous_) { + ARROW_ASSIGN_OR_RAISE(preallocated_, PrepareOutput(total_length)); + } + return Status::OK(); + } + + // If true, and the kernel and output type supports preallocation (for both + // the validity and data buffers), then we allocate one big array and then + // iterate through it while executing the kernel in chunks + bool preallocate_contiguous_ = false; + + // For storing a contiguous preallocation per above. Unused otherwise + std::shared_ptr<ArrayData> preallocated_; +}; + +Status PackBatchNoChunks(const std::vector<Datum>& args, ExecBatch* out) { + int64_t length = 0; + for (const auto& arg : args) { + switch (arg.kind()) { + case Datum::SCALAR: + case Datum::ARRAY: case Datum::CHUNKED_ARRAY: - length = std::max(arg.length(), length); - break; - default: - DCHECK(false); - break; - } - } - out->length = length; - out->values = args; - return Status::OK(); -} - + length = std::max(arg.length(), length); + break; + default: + DCHECK(false); + break; + } + } + out->length = length; + out->values = args; + return Status::OK(); +} + class VectorExecutor : public KernelExecutorImpl<VectorKernel> { - public: - Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { - RETURN_NOT_OK(PrepareExecute(args)); - ExecBatch batch; - if (kernel_->can_execute_chunkwise) { - while (batch_iterator_->Next(&batch)) { - RETURN_NOT_OK(ExecuteBatch(batch, listener)); - } - } else { - RETURN_NOT_OK(PackBatchNoChunks(args, &batch)); - RETURN_NOT_OK(ExecuteBatch(batch, listener)); - } - return Finalize(listener); - } - - Datum WrapResults(const std::vector<Datum>& inputs, - const std::vector<Datum>& outputs) override { - // If execution yielded multiple chunks (because large arrays were split - // based on the ExecContext parameters, then the result is a ChunkedArray + public: + Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { + RETURN_NOT_OK(PrepareExecute(args)); + ExecBatch batch; + if (kernel_->can_execute_chunkwise) { + while (batch_iterator_->Next(&batch)) { + RETURN_NOT_OK(ExecuteBatch(batch, listener)); + } + } else { + RETURN_NOT_OK(PackBatchNoChunks(args, &batch)); + RETURN_NOT_OK(ExecuteBatch(batch, listener)); + } + return Finalize(listener); + } + + Datum WrapResults(const std::vector<Datum>& inputs, + const std::vector<Datum>& outputs) override { + // If execution yielded multiple chunks (because large arrays were split + // based on the ExecContext parameters, then the result is a ChunkedArray if (kernel_->output_chunked && (HaveChunkedArray(inputs) || outputs.size() > 1)) { return ToChunkedArray(outputs, output_descr_.type); } else if (outputs.size() == 1) { // Outputs have just one element return outputs[0]; - } else { + } else { // XXX: In the case where no outputs are omitted, is returning a 0-length // array always the correct move? return MakeArrayOfNull(output_descr_.type, /*length=*/0).ValueOrDie(); - } - } - - protected: - Status ExecuteBatch(const ExecBatch& batch, ExecListener* listener) { - if (batch.length == 0) { - // Skip empty batches. This may only happen when not using - // ExecBatchIterator - return Status::OK(); - } - Datum out; - if (output_descr_.shape == ValueDescr::ARRAY) { - // We preallocate (maybe) only for the output of processing the current - // batch - ARROW_ASSIGN_OR_RAISE(out.value, PrepareOutput(batch.length)); - } - - if (kernel_->null_handling == NullHandling::INTERSECTION && - output_descr_.shape == ValueDescr::ARRAY) { + } + } + + protected: + Status ExecuteBatch(const ExecBatch& batch, ExecListener* listener) { + if (batch.length == 0) { + // Skip empty batches. This may only happen when not using + // ExecBatchIterator + return Status::OK(); + } + Datum out; + if (output_descr_.shape == ValueDescr::ARRAY) { + // We preallocate (maybe) only for the output of processing the current + // batch + ARROW_ASSIGN_OR_RAISE(out.value, PrepareOutput(batch.length)); + } + + if (kernel_->null_handling == NullHandling::INTERSECTION && + output_descr_.shape == ValueDescr::ARRAY) { RETURN_NOT_OK(PropagateNulls(kernel_ctx_, batch, out.mutable_array())); - } + } RETURN_NOT_OK(kernel_->exec(kernel_ctx_, batch, &out)); - if (!kernel_->finalize) { - // If there is no result finalizer (e.g. for hash-based functions, we can - // emit the processed batch right away rather than waiting - RETURN_NOT_OK(listener->OnResult(std::move(out))); - } else { - results_.emplace_back(std::move(out)); - } - return Status::OK(); - } - - Status Finalize(ExecListener* listener) { - if (kernel_->finalize) { - // Intermediate results require post-processing after the execution is - // completed (possibly involving some accumulated state) + if (!kernel_->finalize) { + // If there is no result finalizer (e.g. for hash-based functions, we can + // emit the processed batch right away rather than waiting + RETURN_NOT_OK(listener->OnResult(std::move(out))); + } else { + results_.emplace_back(std::move(out)); + } + return Status::OK(); + } + + Status Finalize(ExecListener* listener) { + if (kernel_->finalize) { + // Intermediate results require post-processing after the execution is + // completed (possibly involving some accumulated state) RETURN_NOT_OK(kernel_->finalize(kernel_ctx_, &results_)); - for (const auto& result : results_) { - RETURN_NOT_OK(listener->OnResult(result)); - } - } - return Status::OK(); - } - - Status SetupArgIteration(const std::vector<Datum>& args) override { - if (kernel_->can_execute_chunkwise) { + for (const auto& result : results_) { + RETURN_NOT_OK(listener->OnResult(result)); + } + } + return Status::OK(); + } + + Status SetupArgIteration(const std::vector<Datum>& args) override { + if (kernel_->can_execute_chunkwise) { ARROW_ASSIGN_OR_RAISE(batch_iterator_, ExecBatchIterator::Make( args, exec_context()->exec_chunksize())); - } - return Status::OK(); - } - - Status PrepareExecute(const std::vector<Datum>& args) { + } + return Status::OK(); + } + + Status PrepareExecute(const std::vector<Datum>& args) { RETURN_NOT_OK(this->SetupArgIteration(args)); - output_num_buffers_ = static_cast<int>(output_descr_.type->layout().buffers.size()); - - // Decide if we need to preallocate memory for this kernel - validity_preallocated_ = - (kernel_->null_handling != NullHandling::COMPUTED_NO_PREALLOCATE && - kernel_->null_handling != NullHandling::OUTPUT_NOT_NULL); + output_num_buffers_ = static_cast<int>(output_descr_.type->layout().buffers.size()); + + // Decide if we need to preallocate memory for this kernel + validity_preallocated_ = + (kernel_->null_handling != NullHandling::COMPUTED_NO_PREALLOCATE && + kernel_->null_handling != NullHandling::OUTPUT_NOT_NULL); if (kernel_->mem_allocation == MemAllocation::PREALLOCATE) { ComputeDataPreallocate(*output_descr_.type, &data_preallocated_); } - return Status::OK(); - } - - std::vector<Datum> results_; -}; - + return Status::OK(); + } + + std::vector<Datum> results_; +}; + class ScalarAggExecutor : public KernelExecutorImpl<ScalarAggregateKernel> { - public: + public: Status Init(KernelContext* ctx, KernelInitArgs args) override { input_descrs_ = &args.inputs; options_ = args.options; return KernelExecutorImpl<ScalarAggregateKernel>::Init(ctx, args); } - - Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { + + Status Execute(const std::vector<Datum>& args, ExecListener* listener) override { RETURN_NOT_OK(this->SetupArgIteration(args)); - - ExecBatch batch; - while (batch_iterator_->Next(&batch)) { - // TODO: implement parallelism - if (batch.length > 0) { - RETURN_NOT_OK(Consume(batch)); - } - } - - Datum out; + + ExecBatch batch; + while (batch_iterator_->Next(&batch)) { + // TODO: implement parallelism + if (batch.length > 0) { + RETURN_NOT_OK(Consume(batch)); + } + } + + Datum out; RETURN_NOT_OK(kernel_->finalize(kernel_ctx_, &out)); - RETURN_NOT_OK(listener->OnResult(std::move(out))); - return Status::OK(); - } - - Datum WrapResults(const std::vector<Datum>&, - const std::vector<Datum>& outputs) override { - DCHECK_EQ(1, outputs.size()); - return outputs[0]; - } - - private: - Status Consume(const ExecBatch& batch) { + RETURN_NOT_OK(listener->OnResult(std::move(out))); + return Status::OK(); + } + + Datum WrapResults(const std::vector<Datum>&, + const std::vector<Datum>& outputs) override { + DCHECK_EQ(1, outputs.size()); + return outputs[0]; + } + + private: + Status Consume(const ExecBatch& batch) { // FIXME(ARROW-11840) don't merge *any* aggegates for every batch ARROW_ASSIGN_OR_RAISE( auto batch_state, kernel_->init(kernel_ctx_, {kernel_, *input_descrs_, options_})); - - if (batch_state == nullptr) { + + if (batch_state == nullptr) { return Status::Invalid("ScalarAggregation requires non-null kernel state"); - } - + } + KernelContext batch_ctx(exec_context()); - batch_ctx.SetState(batch_state.get()); - + batch_ctx.SetState(batch_state.get()); + RETURN_NOT_OK(kernel_->consume(&batch_ctx, batch)); RETURN_NOT_OK(kernel_->merge(kernel_ctx_, std::move(*batch_state), state())); - return Status::OK(); - } + return Status::OK(); + } const std::vector<ValueDescr>* input_descrs_; const FunctionOptions* options_; -}; - -template <typename ExecutorType, - typename FunctionType = typename ExecutorType::FunctionType> +}; + +template <typename ExecutorType, + typename FunctionType = typename ExecutorType::FunctionType> Result<std::unique_ptr<KernelExecutor>> MakeExecutor(ExecContext* ctx, const Function* func, const FunctionOptions* options) { - DCHECK_EQ(ExecutorType::function_kind, func->kind()); - auto typed_func = checked_cast<const FunctionType*>(func); + DCHECK_EQ(ExecutorType::function_kind, func->kind()); + auto typed_func = checked_cast<const FunctionType*>(func); return std::unique_ptr<KernelExecutor>(new ExecutorType(ctx, typed_func, options)); -} - +} + } // namespace Status PropagateNulls(KernelContext* ctx, const ExecBatch& batch, ArrayData* output) { @@ -975,7 +975,7 @@ Status PropagateNulls(KernelContext* ctx, const ExecBatch& batch, ArrayData* out // Null output type is a no-op (rare when this would happen but we at least // will test for it) return Status::OK(); - } + } // This function is ONLY able to write into output with non-zero offset // when the bitmap is preallocated. This could be a DCHECK but returning @@ -987,8 +987,8 @@ Status PropagateNulls(KernelContext* ctx, const ExecBatch& batch, ArrayData* out } NullPropagator propagator(ctx, batch, output); return propagator.Execute(); -} - +} + std::unique_ptr<KernelExecutor> KernelExecutor::MakeScalar() { return ::arrow::internal::make_unique<detail::ScalarExecutor>(); } @@ -1001,50 +1001,50 @@ std::unique_ptr<KernelExecutor> KernelExecutor::MakeScalarAggregate() { return ::arrow::internal::make_unique<detail::ScalarAggExecutor>(); } -} // namespace detail - +} // namespace detail + ExecContext::ExecContext(MemoryPool* pool, ::arrow::internal::Executor* executor, FunctionRegistry* func_registry) : pool_(pool), executor_(executor) { - this->func_registry_ = func_registry == nullptr ? GetFunctionRegistry() : func_registry; -} - -CpuInfo* ExecContext::cpu_info() const { return CpuInfo::GetInstance(); } - -// ---------------------------------------------------------------------- -// SelectionVector - -SelectionVector::SelectionVector(std::shared_ptr<ArrayData> data) - : data_(std::move(data)) { - DCHECK_EQ(Type::INT32, data_->type->id()); - DCHECK_EQ(0, data_->GetNullCount()); - indices_ = data_->GetValues<int32_t>(1); -} - -SelectionVector::SelectionVector(const Array& arr) : SelectionVector(arr.data()) {} - -int32_t SelectionVector::length() const { return static_cast<int32_t>(data_->length); } - -Result<std::shared_ptr<SelectionVector>> SelectionVector::FromMask( - const BooleanArray& arr) { - return Status::NotImplemented("FromMask"); -} - -Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, - const FunctionOptions* options, ExecContext* ctx) { - if (ctx == nullptr) { - ExecContext default_ctx; - return CallFunction(func_name, args, options, &default_ctx); - } - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<const Function> func, - ctx->func_registry()->GetFunction(func_name)); - return func->Execute(args, options, ctx); -} - -Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, - ExecContext* ctx) { - return CallFunction(func_name, args, /*options=*/nullptr, ctx); -} - -} // namespace compute -} // namespace arrow + this->func_registry_ = func_registry == nullptr ? GetFunctionRegistry() : func_registry; +} + +CpuInfo* ExecContext::cpu_info() const { return CpuInfo::GetInstance(); } + +// ---------------------------------------------------------------------- +// SelectionVector + +SelectionVector::SelectionVector(std::shared_ptr<ArrayData> data) + : data_(std::move(data)) { + DCHECK_EQ(Type::INT32, data_->type->id()); + DCHECK_EQ(0, data_->GetNullCount()); + indices_ = data_->GetValues<int32_t>(1); +} + +SelectionVector::SelectionVector(const Array& arr) : SelectionVector(arr.data()) {} + +int32_t SelectionVector::length() const { return static_cast<int32_t>(data_->length); } + +Result<std::shared_ptr<SelectionVector>> SelectionVector::FromMask( + const BooleanArray& arr) { + return Status::NotImplemented("FromMask"); +} + +Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, + const FunctionOptions* options, ExecContext* ctx) { + if (ctx == nullptr) { + ExecContext default_ctx; + return CallFunction(func_name, args, options, &default_ctx); + } + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<const Function> func, + ctx->func_registry()->GetFunction(func_name)); + return func->Execute(args, options, ctx); +} + +Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, + ExecContext* ctx) { + return CallFunction(func_name, args, /*options=*/nullptr, ctx); +} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.h index 227d0c76ad..de1b695de4 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec.h @@ -1,183 +1,183 @@ -// 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. - -// NOTE: API is EXPERIMENTAL and will change without going through a -// deprecation cycle - -#pragma once - -#include <cstdint> -#include <limits> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "arrow/array/data.h" +// 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. + +// NOTE: API is EXPERIMENTAL and will change without going through a +// deprecation cycle + +#pragma once + +#include <cstdint> +#include <limits> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "arrow/array/data.h" #include "arrow/compute/exec/expression.h" -#include "arrow/datum.h" -#include "arrow/memory_pool.h" -#include "arrow/result.h" -#include "arrow/type_fwd.h" -#include "arrow/util/macros.h" +#include "arrow/datum.h" +#include "arrow/memory_pool.h" +#include "arrow/result.h" +#include "arrow/type_fwd.h" +#include "arrow/util/macros.h" #include "arrow/util/type_fwd.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace internal { - -class CpuInfo; - -} // namespace internal - -namespace compute { - +#include "arrow/util/visibility.h" + +namespace arrow { +namespace internal { + +class CpuInfo; + +} // namespace internal + +namespace compute { + class FunctionOptions; -class FunctionRegistry; - -// It seems like 64K might be a good default chunksize to use for execution -// based on the experience of other query processing systems. The current -// default is not to chunk contiguous arrays, though, but this may change in -// the future once parallel execution is implemented -static constexpr int64_t kDefaultExecChunksize = UINT16_MAX; - -/// \brief Context for expression-global variables and options used by -/// function evaluation -class ARROW_EXPORT ExecContext { - public: - // If no function registry passed, the default is used. - explicit ExecContext(MemoryPool* pool = default_memory_pool(), +class FunctionRegistry; + +// It seems like 64K might be a good default chunksize to use for execution +// based on the experience of other query processing systems. The current +// default is not to chunk contiguous arrays, though, but this may change in +// the future once parallel execution is implemented +static constexpr int64_t kDefaultExecChunksize = UINT16_MAX; + +/// \brief Context for expression-global variables and options used by +/// function evaluation +class ARROW_EXPORT ExecContext { + public: + // If no function registry passed, the default is used. + explicit ExecContext(MemoryPool* pool = default_memory_pool(), ::arrow::internal::Executor* executor = NULLPTR, - FunctionRegistry* func_registry = NULLPTR); - - /// \brief The MemoryPool used for allocations, default is - /// default_memory_pool(). - MemoryPool* memory_pool() const { return pool_; } - - ::arrow::internal::CpuInfo* cpu_info() const; - + FunctionRegistry* func_registry = NULLPTR); + + /// \brief The MemoryPool used for allocations, default is + /// default_memory_pool(). + MemoryPool* memory_pool() const { return pool_; } + + ::arrow::internal::CpuInfo* cpu_info() const; + /// \brief An Executor which may be used to parallelize execution. ::arrow::internal::Executor* executor() const { return executor_; } - /// \brief The FunctionRegistry for looking up functions by name and - /// selecting kernels for execution. Defaults to the library-global function - /// registry provided by GetFunctionRegistry. - FunctionRegistry* func_registry() const { return func_registry_; } - - // \brief Set maximum length unit of work for kernel execution. Larger - // contiguous array inputs will be split into smaller chunks, and, if - // possible and enabled, processed in parallel. The default chunksize is - // INT64_MAX, so contiguous arrays are not split. - void set_exec_chunksize(int64_t chunksize) { exec_chunksize_ = chunksize; } - - // \brief Maximum length for ExecBatch data chunks processed by - // kernels. Contiguous array inputs with longer length will be split into - // smaller chunks. - int64_t exec_chunksize() const { return exec_chunksize_; } - - /// \brief Set whether to use multiple threads for function execution. This - /// is not yet used. - void set_use_threads(bool use_threads = true) { use_threads_ = use_threads; } - - /// \brief If true, then utilize multiple threads where relevant for function - /// execution. This is not yet used. - bool use_threads() const { return use_threads_; } - - // Set the preallocation strategy for kernel execution as it relates to - // chunked execution. For chunked execution, whether via ChunkedArray inputs - // or splitting larger Array arguments into smaller pieces, contiguous - // allocation (if permitted by the kernel) will allocate one large array to - // write output into yielding it to the caller at the end. If this option is - // set to off, then preallocations will be performed independently for each - // chunk of execution - // - // TODO: At some point we might want the limit the size of contiguous - // preallocations. For example, even if the exec_chunksize is 64K or less, we - // might limit contiguous allocations to 1M records, say. - void set_preallocate_contiguous(bool preallocate) { - preallocate_contiguous_ = preallocate; - } - - /// \brief If contiguous preallocations should be used when doing chunked - /// execution as specified by exec_chunksize(). See - /// set_preallocate_contiguous() for more information. - bool preallocate_contiguous() const { return preallocate_contiguous_; } - - private: - MemoryPool* pool_; + /// \brief The FunctionRegistry for looking up functions by name and + /// selecting kernels for execution. Defaults to the library-global function + /// registry provided by GetFunctionRegistry. + FunctionRegistry* func_registry() const { return func_registry_; } + + // \brief Set maximum length unit of work for kernel execution. Larger + // contiguous array inputs will be split into smaller chunks, and, if + // possible and enabled, processed in parallel. The default chunksize is + // INT64_MAX, so contiguous arrays are not split. + void set_exec_chunksize(int64_t chunksize) { exec_chunksize_ = chunksize; } + + // \brief Maximum length for ExecBatch data chunks processed by + // kernels. Contiguous array inputs with longer length will be split into + // smaller chunks. + int64_t exec_chunksize() const { return exec_chunksize_; } + + /// \brief Set whether to use multiple threads for function execution. This + /// is not yet used. + void set_use_threads(bool use_threads = true) { use_threads_ = use_threads; } + + /// \brief If true, then utilize multiple threads where relevant for function + /// execution. This is not yet used. + bool use_threads() const { return use_threads_; } + + // Set the preallocation strategy for kernel execution as it relates to + // chunked execution. For chunked execution, whether via ChunkedArray inputs + // or splitting larger Array arguments into smaller pieces, contiguous + // allocation (if permitted by the kernel) will allocate one large array to + // write output into yielding it to the caller at the end. If this option is + // set to off, then preallocations will be performed independently for each + // chunk of execution + // + // TODO: At some point we might want the limit the size of contiguous + // preallocations. For example, even if the exec_chunksize is 64K or less, we + // might limit contiguous allocations to 1M records, say. + void set_preallocate_contiguous(bool preallocate) { + preallocate_contiguous_ = preallocate; + } + + /// \brief If contiguous preallocations should be used when doing chunked + /// execution as specified by exec_chunksize(). See + /// set_preallocate_contiguous() for more information. + bool preallocate_contiguous() const { return preallocate_contiguous_; } + + private: + MemoryPool* pool_; ::arrow::internal::Executor* executor_; - FunctionRegistry* func_registry_; - int64_t exec_chunksize_ = std::numeric_limits<int64_t>::max(); - bool preallocate_contiguous_ = true; - bool use_threads_ = true; -}; - + FunctionRegistry* func_registry_; + int64_t exec_chunksize_ = std::numeric_limits<int64_t>::max(); + bool preallocate_contiguous_ = true; + bool use_threads_ = true; +}; + ARROW_EXPORT ExecContext* default_exec_context(); -// TODO: Consider standardizing on uint16 selection vectors and only use them -// when we can ensure that each value is 64K length or smaller - -/// \brief Container for an array of value selection indices that were -/// materialized from a filter. -/// -/// Columnar query engines (see e.g. [1]) have found that rather than -/// materializing filtered data, the filter can instead be converted to an -/// array of the "on" indices and then "fusing" these indices in operator -/// implementations. This is especially relevant for aggregations but also -/// applies to scalar operations. -/// -/// We are not yet using this so this is mostly a placeholder for now. -/// -/// [1]: http://cidrdb.org/cidr2005/papers/P19.pdf -class ARROW_EXPORT SelectionVector { - public: - explicit SelectionVector(std::shared_ptr<ArrayData> data); - - explicit SelectionVector(const Array& arr); - - /// \brief Create SelectionVector from boolean mask - static Result<std::shared_ptr<SelectionVector>> FromMask(const BooleanArray& arr); - - const int32_t* indices() const { return indices_; } - int32_t length() const; - - private: - std::shared_ptr<ArrayData> data_; - const int32_t* indices_; -}; - -/// \brief A unit of work for kernel execution. It contains a collection of -/// Array and Scalar values and an optional SelectionVector indicating that -/// there is an unmaterialized filter that either must be materialized, or (if -/// the kernel supports it) pushed down into the kernel implementation. -/// -/// ExecBatch is semantically similar to RecordBatch in that in a SQL context -/// it represents a collection of records, but constant "columns" are -/// represented by Scalar values rather than having to be converted into arrays -/// with repeated values. -/// -/// TODO: Datum uses arrow/util/variant.h which may be a bit heavier-weight -/// than is desirable for this class. Microbenchmarks would help determine for -/// sure. See ARROW-8928. +// TODO: Consider standardizing on uint16 selection vectors and only use them +// when we can ensure that each value is 64K length or smaller + +/// \brief Container for an array of value selection indices that were +/// materialized from a filter. +/// +/// Columnar query engines (see e.g. [1]) have found that rather than +/// materializing filtered data, the filter can instead be converted to an +/// array of the "on" indices and then "fusing" these indices in operator +/// implementations. This is especially relevant for aggregations but also +/// applies to scalar operations. +/// +/// We are not yet using this so this is mostly a placeholder for now. +/// +/// [1]: http://cidrdb.org/cidr2005/papers/P19.pdf +class ARROW_EXPORT SelectionVector { + public: + explicit SelectionVector(std::shared_ptr<ArrayData> data); + + explicit SelectionVector(const Array& arr); + + /// \brief Create SelectionVector from boolean mask + static Result<std::shared_ptr<SelectionVector>> FromMask(const BooleanArray& arr); + + const int32_t* indices() const { return indices_; } + int32_t length() const; + + private: + std::shared_ptr<ArrayData> data_; + const int32_t* indices_; +}; + +/// \brief A unit of work for kernel execution. It contains a collection of +/// Array and Scalar values and an optional SelectionVector indicating that +/// there is an unmaterialized filter that either must be materialized, or (if +/// the kernel supports it) pushed down into the kernel implementation. +/// +/// ExecBatch is semantically similar to RecordBatch in that in a SQL context +/// it represents a collection of records, but constant "columns" are +/// represented by Scalar values rather than having to be converted into arrays +/// with repeated values. +/// +/// TODO: Datum uses arrow/util/variant.h which may be a bit heavier-weight +/// than is desirable for this class. Microbenchmarks would help determine for +/// sure. See ARROW-8928. struct ARROW_EXPORT ExecBatch { ExecBatch() = default; - ExecBatch(std::vector<Datum> values, int64_t length) - : values(std::move(values)), length(length) {} - + ExecBatch(std::vector<Datum> values, int64_t length) + : values(std::move(values)), length(length) {} + explicit ExecBatch(const RecordBatch& batch); static Result<ExecBatch> Make(std::vector<Datum> values); @@ -185,80 +185,80 @@ struct ARROW_EXPORT ExecBatch { Result<std::shared_ptr<RecordBatch>> ToRecordBatch( std::shared_ptr<Schema> schema, MemoryPool* pool = default_memory_pool()) const; - /// The values representing positional arguments to be passed to a kernel's - /// exec function for processing. - std::vector<Datum> values; - - /// A deferred filter represented as an array of indices into the values. - /// - /// For example, the filter [true, true, false, true] would be represented as - /// the selection vector [0, 1, 3]. When the selection vector is set, - /// ExecBatch::length is equal to the length of this array. - std::shared_ptr<SelectionVector> selection_vector; - + /// The values representing positional arguments to be passed to a kernel's + /// exec function for processing. + std::vector<Datum> values; + + /// A deferred filter represented as an array of indices into the values. + /// + /// For example, the filter [true, true, false, true] would be represented as + /// the selection vector [0, 1, 3]. When the selection vector is set, + /// ExecBatch::length is equal to the length of this array. + std::shared_ptr<SelectionVector> selection_vector; + /// A predicate Expression guaranteed to evaluate to true for all rows in this batch. Expression guarantee = literal(true); - /// The semantic length of the ExecBatch. When the values are all scalars, - /// the length should be set to 1, otherwise the length is taken from the - /// array values, except when there is a selection vector. When there is a - /// selection vector set, the length of the batch is the length of the - /// selection. - /// - /// If the array values are of length 0 then the length is 0 regardless of - /// whether any values are Scalar. In general ExecBatch objects are produced - /// by ExecBatchIterator which by design does not yield length-0 batches. - int64_t length; - - /// \brief Return the value at the i-th index - template <typename index_type> - inline const Datum& operator[](index_type i) const { - return values[i]; - } - + /// The semantic length of the ExecBatch. When the values are all scalars, + /// the length should be set to 1, otherwise the length is taken from the + /// array values, except when there is a selection vector. When there is a + /// selection vector set, the length of the batch is the length of the + /// selection. + /// + /// If the array values are of length 0 then the length is 0 regardless of + /// whether any values are Scalar. In general ExecBatch objects are produced + /// by ExecBatchIterator which by design does not yield length-0 batches. + int64_t length; + + /// \brief Return the value at the i-th index + template <typename index_type> + inline const Datum& operator[](index_type i) const { + return values[i]; + } + bool Equals(const ExecBatch& other) const; - /// \brief A convenience for the number of values / arguments. - int num_values() const { return static_cast<int>(values.size()); } - + /// \brief A convenience for the number of values / arguments. + int num_values() const { return static_cast<int>(values.size()); } + ExecBatch Slice(int64_t offset, int64_t length) const; - /// \brief A convenience for returning the ValueDescr objects (types and - /// shapes) from the batch. - std::vector<ValueDescr> GetDescriptors() const { - std::vector<ValueDescr> result; - for (const auto& value : this->values) { - result.emplace_back(value.descr()); - } - return result; - } + /// \brief A convenience for returning the ValueDescr objects (types and + /// shapes) from the batch. + std::vector<ValueDescr> GetDescriptors() const { + std::vector<ValueDescr> result; + for (const auto& value : this->values) { + result.emplace_back(value.descr()); + } + return result; + } ARROW_EXPORT friend void PrintTo(const ExecBatch&, std::ostream*); -}; - +}; + inline bool operator==(const ExecBatch& l, const ExecBatch& r) { return l.Equals(r); } inline bool operator!=(const ExecBatch& l, const ExecBatch& r) { return !l.Equals(r); } -/// \defgroup compute-call-function One-shot calls to compute functions -/// -/// @{ - -/// \brief One-shot invoker for all types of functions. -/// -/// Does kernel dispatch, argument checking, iteration of ChunkedArray inputs, -/// and wrapping of outputs. -ARROW_EXPORT -Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, - const FunctionOptions* options, ExecContext* ctx = NULLPTR); - -/// \brief Variant of CallFunction which uses a function's default options. -/// -/// NB: Some functions require FunctionOptions be provided. -ARROW_EXPORT -Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, - ExecContext* ctx = NULLPTR); - -/// @} - -} // namespace compute -} // namespace arrow +/// \defgroup compute-call-function One-shot calls to compute functions +/// +/// @{ + +/// \brief One-shot invoker for all types of functions. +/// +/// Does kernel dispatch, argument checking, iteration of ChunkedArray inputs, +/// and wrapping of outputs. +ARROW_EXPORT +Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, + const FunctionOptions* options, ExecContext* ctx = NULLPTR); + +/// \brief Variant of CallFunction which uses a function's default options. +/// +/// NB: Some functions require FunctionOptions be provided. +ARROW_EXPORT +Result<Datum> CallFunction(const std::string& func_name, const std::vector<Datum>& args, + ExecContext* ctx = NULLPTR); + +/// @} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec_internal.h index e2872e6141..55daa243cd 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/exec_internal.h @@ -1,111 +1,111 @@ -// 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 <cstdint> -#include <limits> -#include <memory> -#include <string> -#include <vector> - -#include "arrow/array.h" -#include "arrow/buffer.h" -#include "arrow/compute/exec.h" -#include "arrow/compute/kernel.h" -#include "arrow/status.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace compute { - -class Function; - -static constexpr int64_t kDefaultMaxChunksize = std::numeric_limits<int64_t>::max(); - -namespace detail { - -/// \brief Break std::vector<Datum> into a sequence of ExecBatch for kernel -/// execution -class ARROW_EXPORT ExecBatchIterator { - public: - /// \brief Construct iterator and do basic argument validation - /// - /// \param[in] args the Datum argument, must be all array-like or scalar - /// \param[in] max_chunksize the maximum length of each ExecBatch. Depending - /// on the chunk layout of ChunkedArray. - static Result<std::unique_ptr<ExecBatchIterator>> Make( - std::vector<Datum> args, int64_t max_chunksize = kDefaultMaxChunksize); - - /// \brief Compute the next batch. Always returns at least one batch. Return - /// false if the iterator is exhausted - bool Next(ExecBatch* batch); - - int64_t length() const { return length_; } - - int64_t position() const { return position_; } - - int64_t max_chunksize() const { return max_chunksize_; } - - private: - ExecBatchIterator(std::vector<Datum> args, int64_t length, int64_t max_chunksize); - - std::vector<Datum> args_; - std::vector<int> chunk_indexes_; - std::vector<int64_t> chunk_positions_; - int64_t position_; - int64_t length_; - int64_t max_chunksize_; -}; - -// "Push" / listener API like IPC reader so that consumers can receive -// processed chunks as soon as they're available. - -class ARROW_EXPORT ExecListener { - public: - virtual ~ExecListener() = default; - - virtual Status OnResult(Datum) { return Status::NotImplemented("OnResult"); } -}; - -class DatumAccumulator : public ExecListener { - public: +// 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 <cstdint> +#include <limits> +#include <memory> +#include <string> +#include <vector> + +#include "arrow/array.h" +#include "arrow/buffer.h" +#include "arrow/compute/exec.h" +#include "arrow/compute/kernel.h" +#include "arrow/status.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace compute { + +class Function; + +static constexpr int64_t kDefaultMaxChunksize = std::numeric_limits<int64_t>::max(); + +namespace detail { + +/// \brief Break std::vector<Datum> into a sequence of ExecBatch for kernel +/// execution +class ARROW_EXPORT ExecBatchIterator { + public: + /// \brief Construct iterator and do basic argument validation + /// + /// \param[in] args the Datum argument, must be all array-like or scalar + /// \param[in] max_chunksize the maximum length of each ExecBatch. Depending + /// on the chunk layout of ChunkedArray. + static Result<std::unique_ptr<ExecBatchIterator>> Make( + std::vector<Datum> args, int64_t max_chunksize = kDefaultMaxChunksize); + + /// \brief Compute the next batch. Always returns at least one batch. Return + /// false if the iterator is exhausted + bool Next(ExecBatch* batch); + + int64_t length() const { return length_; } + + int64_t position() const { return position_; } + + int64_t max_chunksize() const { return max_chunksize_; } + + private: + ExecBatchIterator(std::vector<Datum> args, int64_t length, int64_t max_chunksize); + + std::vector<Datum> args_; + std::vector<int> chunk_indexes_; + std::vector<int64_t> chunk_positions_; + int64_t position_; + int64_t length_; + int64_t max_chunksize_; +}; + +// "Push" / listener API like IPC reader so that consumers can receive +// processed chunks as soon as they're available. + +class ARROW_EXPORT ExecListener { + public: + virtual ~ExecListener() = default; + + virtual Status OnResult(Datum) { return Status::NotImplemented("OnResult"); } +}; + +class DatumAccumulator : public ExecListener { + public: DatumAccumulator() = default; - - Status OnResult(Datum value) override { - values_.emplace_back(value); - return Status::OK(); - } - + + Status OnResult(Datum value) override { + values_.emplace_back(value); + return Status::OK(); + } + std::vector<Datum> values() { return std::move(values_); } - - private: - std::vector<Datum> values_; -}; - -/// \brief Check that each Datum is of a "value" type, which means either -/// SCALAR, ARRAY, or CHUNKED_ARRAY. If there are chunked inputs, then these -/// inputs will be split into non-chunked ExecBatch values for execution -Status CheckAllValues(const std::vector<Datum>& values); - + + private: + std::vector<Datum> values_; +}; + +/// \brief Check that each Datum is of a "value" type, which means either +/// SCALAR, ARRAY, or CHUNKED_ARRAY. If there are chunked inputs, then these +/// inputs will be split into non-chunked ExecBatch values for execution +Status CheckAllValues(const std::vector<Datum>& values); + class ARROW_EXPORT KernelExecutor { - public: + public: virtual ~KernelExecutor() = default; - + /// The Kernel's `init` method must be called and any KernelState set in the /// KernelContext *before* KernelExecutor::Init is called. This is to facilitate /// the case where init may be expensive and does not need to be called again for @@ -113,30 +113,30 @@ class ARROW_EXPORT KernelExecutor { /// for all scanned batches in a dataset filter. virtual Status Init(KernelContext*, KernelInitArgs) = 0; - /// XXX: Better configurability for listener - /// Not thread-safe - virtual Status Execute(const std::vector<Datum>& args, ExecListener* listener) = 0; - - virtual Datum WrapResults(const std::vector<Datum>& args, - const std::vector<Datum>& outputs) = 0; - + /// XXX: Better configurability for listener + /// Not thread-safe + virtual Status Execute(const std::vector<Datum>& args, ExecListener* listener) = 0; + + virtual Datum WrapResults(const std::vector<Datum>& args, + const std::vector<Datum>& outputs) = 0; + static std::unique_ptr<KernelExecutor> MakeScalar(); static std::unique_ptr<KernelExecutor> MakeVector(); static std::unique_ptr<KernelExecutor> MakeScalarAggregate(); -}; - -/// \brief Populate validity bitmap with the intersection of the nullity of the -/// arguments. If a preallocated bitmap is not provided, then one will be -/// allocated if needed (in some cases a bitmap can be zero-copied from the -/// arguments). If any Scalar value is null, then the entire validity bitmap -/// will be set to null. -/// -/// \param[in] ctx kernel execution context, for memory allocation etc. -/// \param[in] batch the data batch -/// \param[in] out the output ArrayData, must not be null -ARROW_EXPORT -Status PropagateNulls(KernelContext* ctx, const ExecBatch& batch, ArrayData* out); - -} // namespace detail -} // namespace compute -} // namespace arrow +}; + +/// \brief Populate validity bitmap with the intersection of the nullity of the +/// arguments. If a preallocated bitmap is not provided, then one will be +/// allocated if needed (in some cases a bitmap can be zero-copied from the +/// arguments). If any Scalar value is null, then the entire validity bitmap +/// will be set to null. +/// +/// \param[in] ctx kernel execution context, for memory allocation etc. +/// \param[in] batch the data batch +/// \param[in] out the output ArrayData, must not be null +ARROW_EXPORT +Status PropagateNulls(KernelContext* ctx, const ExecBatch& batch, ArrayData* out); + +} // namespace detail +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.cc index 3eefb327c1..05d14d03b1 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.cc @@ -1,46 +1,46 @@ -// 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. - -#include "arrow/compute/function.h" - -#include <cstddef> -#include <memory> -#include <sstream> - +// 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. + +#include "arrow/compute/function.h" + +#include <cstddef> +#include <memory> +#include <sstream> + #include "arrow/compute/api_scalar.h" #include "arrow/compute/cast.h" -#include "arrow/compute/exec.h" -#include "arrow/compute/exec_internal.h" +#include "arrow/compute/exec.h" +#include "arrow/compute/exec_internal.h" #include "arrow/compute/function_internal.h" #include "arrow/compute/kernels/common.h" #include "arrow/compute/registry.h" -#include "arrow/datum.h" -#include "arrow/util/cpu_info.h" - -namespace arrow { +#include "arrow/datum.h" +#include "arrow/util/cpu_info.h" + +namespace arrow { using internal::checked_cast; -namespace compute { +namespace compute { Result<std::shared_ptr<Buffer>> FunctionOptionsType::Serialize( const FunctionOptions&) const { return Status::NotImplemented("Serialize for ", type_name()); } - + Result<std::unique_ptr<FunctionOptions>> FunctionOptionsType::Deserialize( const Buffer& buffer) const { return Status::NotImplemented("Deserialize for ", type_name()); @@ -79,7 +79,7 @@ static Status CheckArityImpl(const Function* function, int passed_num_args, return Status::Invalid("VarArgs function ", function->name(), " needs at least ", function->arity().num_args, " arguments but ", passed_num_args_label, " only ", passed_num_args); - } + } if (!function->arity().is_varargs && passed_num_args != function->arity().num_args) { return Status::Invalid("Function ", function->name(), " accepts ", @@ -87,18 +87,18 @@ static Status CheckArityImpl(const Function* function, int passed_num_args, passed_num_args_label, " ", passed_num_args); } - return Status::OK(); -} - + return Status::OK(); +} + Status Function::CheckArity(const std::vector<InputType>& in_types) const { return CheckArityImpl(this, static_cast<int>(in_types.size()), "kernel accepts"); -} - +} + Status Function::CheckArity(const std::vector<ValueDescr>& descrs) const { return CheckArityImpl(this, static_cast<int>(descrs.size()), "attempted to look up kernel(s) with"); } - + namespace detail { Status NoMatchingKernel(const Function* func, const std::vector<ValueDescr>& descrs) { @@ -112,38 +112,38 @@ const KernelType* DispatchExactImpl(const std::vector<KernelType*>& kernels, const std::vector<ValueDescr>& values) { const KernelType* kernel_matches[SimdLevel::MAX] = {nullptr}; - // Validate arity - for (const auto& kernel : kernels) { + // Validate arity + for (const auto& kernel : kernels) { if (kernel->signature->MatchesInputs(values)) { kernel_matches[kernel->simd_level] = kernel; - } - } - - // Dispatch as the CPU feature + } + } + + // Dispatch as the CPU feature #if defined(ARROW_HAVE_RUNTIME_AVX512) || defined(ARROW_HAVE_RUNTIME_AVX2) - auto cpu_info = arrow::internal::CpuInfo::GetInstance(); + auto cpu_info = arrow::internal::CpuInfo::GetInstance(); +#endif +#if defined(ARROW_HAVE_RUNTIME_AVX512) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { + if (kernel_matches[SimdLevel::AVX512]) { + return kernel_matches[SimdLevel::AVX512]; + } + } #endif -#if defined(ARROW_HAVE_RUNTIME_AVX512) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { - if (kernel_matches[SimdLevel::AVX512]) { - return kernel_matches[SimdLevel::AVX512]; - } - } -#endif -#if defined(ARROW_HAVE_RUNTIME_AVX2) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { - if (kernel_matches[SimdLevel::AVX2]) { - return kernel_matches[SimdLevel::AVX2]; - } - } -#endif - if (kernel_matches[SimdLevel::NONE]) { - return kernel_matches[SimdLevel::NONE]; - } - +#if defined(ARROW_HAVE_RUNTIME_AVX2) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { + if (kernel_matches[SimdLevel::AVX2]) { + return kernel_matches[SimdLevel::AVX2]; + } + } +#endif + if (kernel_matches[SimdLevel::NONE]) { + return kernel_matches[SimdLevel::NONE]; + } + return nullptr; -} - +} + const Kernel* DispatchExactImpl(const Function* func, const std::vector<ValueDescr>& values) { if (func->kind() == Function::SCALAR) { @@ -189,19 +189,19 @@ Result<const Kernel*> Function::DispatchBest(std::vector<ValueDescr>* values) co return DispatchExact(*values); } -Result<Datum> Function::Execute(const std::vector<Datum>& args, - const FunctionOptions* options, ExecContext* ctx) const { - if (options == nullptr) { - options = default_options(); - } - if (ctx == nullptr) { - ExecContext default_ctx; - return Execute(args, options, &default_ctx); - } - - // type-check Datum arguments here. Really we'd like to avoid this as much as - // possible - RETURN_NOT_OK(detail::CheckAllValues(args)); +Result<Datum> Function::Execute(const std::vector<Datum>& args, + const FunctionOptions* options, ExecContext* ctx) const { + if (options == nullptr) { + options = default_options(); + } + if (ctx == nullptr) { + ExecContext default_ctx; + return Execute(args, options, &default_ctx); + } + + // type-check Datum arguments here. Really we'd like to avoid this as much as + // possible + RETURN_NOT_OK(detail::CheckAllValues(args)); std::vector<ValueDescr> inputs(args.size()); for (size_t i = 0; i != args.size(); ++i) { inputs[i] = args[i].descr(); @@ -230,11 +230,11 @@ Result<Datum> Function::Execute(const std::vector<Datum>& args, } RETURN_NOT_OK(executor->Init(&kernel_ctx, {kernel, inputs, options})); - auto listener = std::make_shared<detail::DatumAccumulator>(); + auto listener = std::make_shared<detail::DatumAccumulator>(); RETURN_NOT_OK(executor->Execute(implicitly_cast_args, listener.get())); return executor->WrapResults(implicitly_cast_args, listener->values()); -} - +} + Status Function::Validate() const { if (!doc_->summary.empty()) { // Documentation given, check its contents @@ -252,59 +252,59 @@ Status Function::Validate() const { return Status::OK(); } -Status ScalarFunction::AddKernel(std::vector<InputType> in_types, OutputType out_type, - ArrayKernelExec exec, KernelInit init) { +Status ScalarFunction::AddKernel(std::vector<InputType> in_types, OutputType out_type, + ArrayKernelExec exec, KernelInit init) { RETURN_NOT_OK(CheckArity(in_types)); - - if (arity_.is_varargs && in_types.size() != 1) { - return Status::Invalid("VarArgs signatures must have exactly one input type"); - } - auto sig = - KernelSignature::Make(std::move(in_types), std::move(out_type), arity_.is_varargs); - kernels_.emplace_back(std::move(sig), exec, init); - return Status::OK(); -} - -Status ScalarFunction::AddKernel(ScalarKernel kernel) { + + if (arity_.is_varargs && in_types.size() != 1) { + return Status::Invalid("VarArgs signatures must have exactly one input type"); + } + auto sig = + KernelSignature::Make(std::move(in_types), std::move(out_type), arity_.is_varargs); + kernels_.emplace_back(std::move(sig), exec, init); + return Status::OK(); +} + +Status ScalarFunction::AddKernel(ScalarKernel kernel) { RETURN_NOT_OK(CheckArity(kernel.signature->in_types())); - if (arity_.is_varargs && !kernel.signature->is_varargs()) { - return Status::Invalid("Function accepts varargs but kernel signature does not"); - } - kernels_.emplace_back(std::move(kernel)); - return Status::OK(); -} - -Status VectorFunction::AddKernel(std::vector<InputType> in_types, OutputType out_type, - ArrayKernelExec exec, KernelInit init) { + if (arity_.is_varargs && !kernel.signature->is_varargs()) { + return Status::Invalid("Function accepts varargs but kernel signature does not"); + } + kernels_.emplace_back(std::move(kernel)); + return Status::OK(); +} + +Status VectorFunction::AddKernel(std::vector<InputType> in_types, OutputType out_type, + ArrayKernelExec exec, KernelInit init) { RETURN_NOT_OK(CheckArity(in_types)); - - if (arity_.is_varargs && in_types.size() != 1) { - return Status::Invalid("VarArgs signatures must have exactly one input type"); - } - auto sig = - KernelSignature::Make(std::move(in_types), std::move(out_type), arity_.is_varargs); - kernels_.emplace_back(std::move(sig), exec, init); - return Status::OK(); -} - -Status VectorFunction::AddKernel(VectorKernel kernel) { + + if (arity_.is_varargs && in_types.size() != 1) { + return Status::Invalid("VarArgs signatures must have exactly one input type"); + } + auto sig = + KernelSignature::Make(std::move(in_types), std::move(out_type), arity_.is_varargs); + kernels_.emplace_back(std::move(sig), exec, init); + return Status::OK(); +} + +Status VectorFunction::AddKernel(VectorKernel kernel) { RETURN_NOT_OK(CheckArity(kernel.signature->in_types())); - if (arity_.is_varargs && !kernel.signature->is_varargs()) { - return Status::Invalid("Function accepts varargs but kernel signature does not"); - } - kernels_.emplace_back(std::move(kernel)); - return Status::OK(); -} - -Status ScalarAggregateFunction::AddKernel(ScalarAggregateKernel kernel) { + if (arity_.is_varargs && !kernel.signature->is_varargs()) { + return Status::Invalid("Function accepts varargs but kernel signature does not"); + } + kernels_.emplace_back(std::move(kernel)); + return Status::OK(); +} + +Status ScalarAggregateFunction::AddKernel(ScalarAggregateKernel kernel) { RETURN_NOT_OK(CheckArity(kernel.signature->in_types())); - if (arity_.is_varargs && !kernel.signature->is_varargs()) { - return Status::Invalid("Function accepts varargs but kernel signature does not"); - } - kernels_.emplace_back(std::move(kernel)); - return Status::OK(); -} - + if (arity_.is_varargs && !kernel.signature->is_varargs()) { + return Status::Invalid("Function accepts varargs but kernel signature does not"); + } + kernels_.emplace_back(std::move(kernel)); + return Status::OK(); +} + Status HashAggregateFunction::AddKernel(HashAggregateKernel kernel) { RETURN_NOT_OK(CheckArity(kernel.signature->in_types())); if (arity_.is_varargs && !kernel.signature->is_varargs()) { @@ -312,19 +312,19 @@ Status HashAggregateFunction::AddKernel(HashAggregateKernel kernel) { } kernels_.emplace_back(std::move(kernel)); return Status::OK(); -} - -Result<Datum> MetaFunction::Execute(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const { +} + +Result<Datum> MetaFunction::Execute(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const { RETURN_NOT_OK( CheckArityImpl(this, static_cast<int>(args.size()), "attempted to Execute with")); - if (options == nullptr) { - options = default_options(); - } - return ExecuteImpl(args, options, ctx); -} - -} // namespace compute -} // namespace arrow + if (options == nullptr) { + options = default_options(); + } + return ExecuteImpl(args, options, ctx); +} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.h index 69c55cd998..bd854bbb28 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/function.h @@ -1,45 +1,45 @@ -// 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. - -// NOTE: API is EXPERIMENTAL and will change without going through a -// deprecation cycle. - -#pragma once - -#include <string> -#include <utility> -#include <vector> - -#include "arrow/compute/kernel.h" -#include "arrow/compute/type_fwd.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/status.h" +// 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. + +// NOTE: API is EXPERIMENTAL and will change without going through a +// deprecation cycle. + +#pragma once + +#include <string> +#include <utility> +#include <vector> + +#include "arrow/compute/kernel.h" +#include "arrow/compute/type_fwd.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/status.h" #include "arrow/util/compare.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace compute { - -/// \defgroup compute-functions Abstract compute function API -/// -/// @{ - +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace compute { + +/// \defgroup compute-functions Abstract compute function API +/// +/// @{ + /// \brief Extension point for defining options outside libarrow (but /// still within this project). class ARROW_EXPORT FunctionOptionsType { @@ -54,12 +54,12 @@ class ARROW_EXPORT FunctionOptionsType { const Buffer& buffer) const; }; -/// \brief Base class for specifying options configuring a function's behavior, -/// such as error handling. +/// \brief Base class for specifying options configuring a function's behavior, +/// such as error handling. class ARROW_EXPORT FunctionOptions : public util::EqualityComparable<FunctionOptions> { public: virtual ~FunctionOptions() = default; - + const FunctionOptionsType* options_type() const { return options_type_; } const char* type_name() const { return options_type()->type_name(); } @@ -84,40 +84,40 @@ class ARROW_EXPORT FunctionOptions : public util::EqualityComparable<FunctionOpt ARROW_EXPORT void PrintTo(const FunctionOptions&, std::ostream*); -/// \brief Contains the number of required arguments for the function. -/// -/// Naming conventions taken from https://en.wikipedia.org/wiki/Arity. -struct ARROW_EXPORT Arity { - /// \brief A function taking no arguments - static Arity Nullary() { return Arity(0, false); } - - /// \brief A function taking 1 argument - static Arity Unary() { return Arity(1, false); } - - /// \brief A function taking 2 arguments - static Arity Binary() { return Arity(2, false); } - - /// \brief A function taking 3 arguments - static Arity Ternary() { return Arity(3, false); } - - /// \brief A function taking a variable number of arguments - /// - /// \param[in] min_args the minimum number of arguments required when - /// invoking the function - static Arity VarArgs(int min_args = 0) { return Arity(min_args, true); } - - // NOTE: the 0-argument form (default constructor) is required for Cython - explicit Arity(int num_args = 0, bool is_varargs = false) - : num_args(num_args), is_varargs(is_varargs) {} - - /// The number of required arguments (or the minimum number for varargs - /// functions). - int num_args; - - /// If true, then the num_args is the minimum number of required arguments. - bool is_varargs = false; -}; - +/// \brief Contains the number of required arguments for the function. +/// +/// Naming conventions taken from https://en.wikipedia.org/wiki/Arity. +struct ARROW_EXPORT Arity { + /// \brief A function taking no arguments + static Arity Nullary() { return Arity(0, false); } + + /// \brief A function taking 1 argument + static Arity Unary() { return Arity(1, false); } + + /// \brief A function taking 2 arguments + static Arity Binary() { return Arity(2, false); } + + /// \brief A function taking 3 arguments + static Arity Ternary() { return Arity(3, false); } + + /// \brief A function taking a variable number of arguments + /// + /// \param[in] min_args the minimum number of arguments required when + /// invoking the function + static Arity VarArgs(int min_args = 0) { return Arity(min_args, true); } + + // NOTE: the 0-argument form (default constructor) is required for Cython + explicit Arity(int num_args = 0, bool is_varargs = false) + : num_args(num_args), is_varargs(is_varargs) {} + + /// The number of required arguments (or the minimum number for varargs + /// functions). + int num_args; + + /// If true, then the num_args is the minimum number of required arguments. + bool is_varargs = false; +}; + struct ARROW_EXPORT FunctionDoc { /// \brief A one-line summary of the function, using a verb. /// @@ -149,57 +149,57 @@ struct ARROW_EXPORT FunctionDoc { static const FunctionDoc& Empty(); }; -/// \brief Base class for compute functions. Function implementations contain a -/// collection of "kernels" which are implementations of the function for -/// specific argument types. Selecting a viable kernel for executing a function -/// is referred to as "dispatching". -class ARROW_EXPORT Function { - public: - /// \brief The kind of function, which indicates in what contexts it is - /// valid for use. - enum Kind { - /// A function that performs scalar data operations on whole arrays of - /// data. Can generally process Array or Scalar values. The size of the - /// output will be the same as the size (or broadcasted size, in the case - /// of mixing Array and Scalar inputs) of the input. - SCALAR, - - /// A function with array input and output whose behavior depends on the - /// values of the entire arrays passed, rather than the value of each scalar - /// value. - VECTOR, - - /// A function that computes scalar summary statistics from array input. - SCALAR_AGGREGATE, - +/// \brief Base class for compute functions. Function implementations contain a +/// collection of "kernels" which are implementations of the function for +/// specific argument types. Selecting a viable kernel for executing a function +/// is referred to as "dispatching". +class ARROW_EXPORT Function { + public: + /// \brief The kind of function, which indicates in what contexts it is + /// valid for use. + enum Kind { + /// A function that performs scalar data operations on whole arrays of + /// data. Can generally process Array or Scalar values. The size of the + /// output will be the same as the size (or broadcasted size, in the case + /// of mixing Array and Scalar inputs) of the input. + SCALAR, + + /// A function with array input and output whose behavior depends on the + /// values of the entire arrays passed, rather than the value of each scalar + /// value. + VECTOR, + + /// A function that computes scalar summary statistics from array input. + SCALAR_AGGREGATE, + /// A function that computes grouped summary statistics from array input /// and an array of group identifiers. HASH_AGGREGATE, - /// A function that dispatches to other functions and does not contain its - /// own kernels. - META - }; - - virtual ~Function() = default; - - /// \brief The name of the kernel. The registry enforces uniqueness of names. - const std::string& name() const { return name_; } - - /// \brief The kind of kernel, which indicates in what contexts it is valid - /// for use. - Function::Kind kind() const { return kind_; } - - /// \brief Contains the number of arguments the function requires, or if the - /// function accepts variable numbers of arguments. - const Arity& arity() const { return arity_; } - + /// A function that dispatches to other functions and does not contain its + /// own kernels. + META + }; + + virtual ~Function() = default; + + /// \brief The name of the kernel. The registry enforces uniqueness of names. + const std::string& name() const { return name_; } + + /// \brief The kind of kernel, which indicates in what contexts it is valid + /// for use. + Function::Kind kind() const { return kind_; } + + /// \brief Contains the number of arguments the function requires, or if the + /// function accepts variable numbers of arguments. + const Arity& arity() const { return arity_; } + /// \brief Return the function documentation const FunctionDoc& doc() const { return *doc_; } - /// \brief Returns the number of registered kernels for this function. - virtual int num_kernels() const = 0; - + /// \brief Returns the number of registered kernels for this function. + virtual int num_kernels() const = 0; + /// \brief Return a kernel that can execute the function given the exact /// argument types (without implicit type casts or scalar->array promotions). /// @@ -215,67 +215,67 @@ class ARROW_EXPORT Function { /// are responsible for casting inputs to the type and shape required by the kernel. virtual Result<const Kernel*> DispatchBest(std::vector<ValueDescr>* values) const; - /// \brief Execute the function eagerly with the passed input arguments with - /// kernel dispatch, batch iteration, and memory allocation details taken - /// care of. - /// - /// If the `options` pointer is null, then `default_options()` will be used. - /// - /// This function can be overridden in subclasses. - virtual Result<Datum> Execute(const std::vector<Datum>& args, - const FunctionOptions* options, ExecContext* ctx) const; - - /// \brief Returns a the default options for this function. - /// - /// Whatever option semantics a Function has, implementations must guarantee - /// that default_options() is valid to pass to Execute as options. - const FunctionOptions* default_options() const { return default_options_; } - + /// \brief Execute the function eagerly with the passed input arguments with + /// kernel dispatch, batch iteration, and memory allocation details taken + /// care of. + /// + /// If the `options` pointer is null, then `default_options()` will be used. + /// + /// This function can be overridden in subclasses. + virtual Result<Datum> Execute(const std::vector<Datum>& args, + const FunctionOptions* options, ExecContext* ctx) const; + + /// \brief Returns a the default options for this function. + /// + /// Whatever option semantics a Function has, implementations must guarantee + /// that default_options() is valid to pass to Execute as options. + const FunctionOptions* default_options() const { return default_options_; } + virtual Status Validate() const; - protected: - Function(std::string name, Function::Kind kind, const Arity& arity, + protected: + Function(std::string name, Function::Kind kind, const Arity& arity, const FunctionDoc* doc, const FunctionOptions* default_options) - : name_(std::move(name)), - kind_(kind), - arity_(arity), + : name_(std::move(name)), + kind_(kind), + arity_(arity), doc_(doc ? doc : &FunctionDoc::Empty()), - default_options_(default_options) {} - + default_options_(default_options) {} + Status CheckArity(const std::vector<InputType>&) const; Status CheckArity(const std::vector<ValueDescr>&) const; - - std::string name_; - Function::Kind kind_; - Arity arity_; + + std::string name_; + Function::Kind kind_; + Arity arity_; const FunctionDoc* doc_; - const FunctionOptions* default_options_ = NULLPTR; -}; - -namespace detail { - -template <typename KernelType> -class FunctionImpl : public Function { - public: - /// \brief Return pointers to current-available kernels for inspection - std::vector<const KernelType*> kernels() const { - std::vector<const KernelType*> result; - for (const auto& kernel : kernels_) { - result.push_back(&kernel); - } - return result; - } - - int num_kernels() const override { return static_cast<int>(kernels_.size()); } - - protected: - FunctionImpl(std::string name, Function::Kind kind, const Arity& arity, + const FunctionOptions* default_options_ = NULLPTR; +}; + +namespace detail { + +template <typename KernelType> +class FunctionImpl : public Function { + public: + /// \brief Return pointers to current-available kernels for inspection + std::vector<const KernelType*> kernels() const { + std::vector<const KernelType*> result; + for (const auto& kernel : kernels_) { + result.push_back(&kernel); + } + return result; + } + + int num_kernels() const override { return static_cast<int>(kernels_.size()); } + + protected: + FunctionImpl(std::string name, Function::Kind kind, const Arity& arity, const FunctionDoc* doc, const FunctionOptions* default_options) : Function(std::move(name), kind, arity, doc, default_options) {} - - std::vector<KernelType> kernels_; -}; - + + std::vector<KernelType> kernels_; +}; + /// \brief Look up a kernel in a function. If no Kernel is found, nullptr is returned. ARROW_EXPORT const Kernel* DispatchExactImpl(const Function* func, const std::vector<ValueDescr>&); @@ -284,72 +284,72 @@ const Kernel* DispatchExactImpl(const Function* func, const std::vector<ValueDes ARROW_EXPORT Status NoMatchingKernel(const Function* func, const std::vector<ValueDescr>&); -} // namespace detail - -/// \brief A function that executes elementwise operations on arrays or -/// scalars, and therefore whose results generally do not depend on the order -/// of the values in the arguments. Accepts and returns arrays that are all of -/// the same size. These functions roughly correspond to the functions used in -/// SQL expressions. -class ARROW_EXPORT ScalarFunction : public detail::FunctionImpl<ScalarKernel> { - public: - using KernelType = ScalarKernel; - +} // namespace detail + +/// \brief A function that executes elementwise operations on arrays or +/// scalars, and therefore whose results generally do not depend on the order +/// of the values in the arguments. Accepts and returns arrays that are all of +/// the same size. These functions roughly correspond to the functions used in +/// SQL expressions. +class ARROW_EXPORT ScalarFunction : public detail::FunctionImpl<ScalarKernel> { + public: + using KernelType = ScalarKernel; + ScalarFunction(std::string name, const Arity& arity, const FunctionDoc* doc, - const FunctionOptions* default_options = NULLPTR) + const FunctionOptions* default_options = NULLPTR) : detail::FunctionImpl<ScalarKernel>(std::move(name), Function::SCALAR, arity, doc, - default_options) {} - - /// \brief Add a kernel with given input/output types, no required state - /// initialization, preallocation for fixed-width types, and default null - /// handling (intersect validity bitmaps of inputs). - Status AddKernel(std::vector<InputType> in_types, OutputType out_type, - ArrayKernelExec exec, KernelInit init = NULLPTR); - - /// \brief Add a kernel (function implementation). Returns error if the - /// kernel's signature does not match the function's arity. - Status AddKernel(ScalarKernel kernel); -}; - -/// \brief A function that executes general array operations that may yield -/// outputs of different sizes or have results that depend on the whole array -/// contents. These functions roughly correspond to the functions found in -/// non-SQL array languages like APL and its derivatives. -class ARROW_EXPORT VectorFunction : public detail::FunctionImpl<VectorKernel> { - public: - using KernelType = VectorKernel; - + default_options) {} + + /// \brief Add a kernel with given input/output types, no required state + /// initialization, preallocation for fixed-width types, and default null + /// handling (intersect validity bitmaps of inputs). + Status AddKernel(std::vector<InputType> in_types, OutputType out_type, + ArrayKernelExec exec, KernelInit init = NULLPTR); + + /// \brief Add a kernel (function implementation). Returns error if the + /// kernel's signature does not match the function's arity. + Status AddKernel(ScalarKernel kernel); +}; + +/// \brief A function that executes general array operations that may yield +/// outputs of different sizes or have results that depend on the whole array +/// contents. These functions roughly correspond to the functions found in +/// non-SQL array languages like APL and its derivatives. +class ARROW_EXPORT VectorFunction : public detail::FunctionImpl<VectorKernel> { + public: + using KernelType = VectorKernel; + VectorFunction(std::string name, const Arity& arity, const FunctionDoc* doc, - const FunctionOptions* default_options = NULLPTR) + const FunctionOptions* default_options = NULLPTR) : detail::FunctionImpl<VectorKernel>(std::move(name), Function::VECTOR, arity, doc, - default_options) {} - - /// \brief Add a simple kernel with given input/output types, no required - /// state initialization, no data preallocation, and no preallocation of the - /// validity bitmap. - Status AddKernel(std::vector<InputType> in_types, OutputType out_type, - ArrayKernelExec exec, KernelInit init = NULLPTR); - - /// \brief Add a kernel (function implementation). Returns error if the - /// kernel's signature does not match the function's arity. - Status AddKernel(VectorKernel kernel); -}; - -class ARROW_EXPORT ScalarAggregateFunction - : public detail::FunctionImpl<ScalarAggregateKernel> { - public: - using KernelType = ScalarAggregateKernel; - + default_options) {} + + /// \brief Add a simple kernel with given input/output types, no required + /// state initialization, no data preallocation, and no preallocation of the + /// validity bitmap. + Status AddKernel(std::vector<InputType> in_types, OutputType out_type, + ArrayKernelExec exec, KernelInit init = NULLPTR); + + /// \brief Add a kernel (function implementation). Returns error if the + /// kernel's signature does not match the function's arity. + Status AddKernel(VectorKernel kernel); +}; + +class ARROW_EXPORT ScalarAggregateFunction + : public detail::FunctionImpl<ScalarAggregateKernel> { + public: + using KernelType = ScalarAggregateKernel; + ScalarAggregateFunction(std::string name, const Arity& arity, const FunctionDoc* doc, - const FunctionOptions* default_options = NULLPTR) - : detail::FunctionImpl<ScalarAggregateKernel>( + const FunctionOptions* default_options = NULLPTR) + : detail::FunctionImpl<ScalarAggregateKernel>( std::move(name), Function::SCALAR_AGGREGATE, arity, doc, default_options) {} - - /// \brief Add a kernel (function implementation). Returns error if the - /// kernel's signature does not match the function's arity. - Status AddKernel(ScalarAggregateKernel kernel); + + /// \brief Add a kernel (function implementation). Returns error if the + /// kernel's signature does not match the function's arity. + Status AddKernel(ScalarAggregateKernel kernel); }; - + class ARROW_EXPORT HashAggregateFunction : public detail::FunctionImpl<HashAggregateKernel> { public: @@ -363,31 +363,31 @@ class ARROW_EXPORT HashAggregateFunction /// \brief Add a kernel (function implementation). Returns error if the /// kernel's signature does not match the function's arity. Status AddKernel(HashAggregateKernel kernel); -}; - -/// \brief A function that dispatches to other functions. Must implement -/// MetaFunction::ExecuteImpl. -/// -/// For Array, ChunkedArray, and Scalar Datum kinds, may rely on the execution -/// of concrete Function types, but must handle other Datum kinds on its own. -class ARROW_EXPORT MetaFunction : public Function { - public: - int num_kernels() const override { return 0; } - - Result<Datum> Execute(const std::vector<Datum>& args, const FunctionOptions* options, - ExecContext* ctx) const override; - - protected: - virtual Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const = 0; - +}; + +/// \brief A function that dispatches to other functions. Must implement +/// MetaFunction::ExecuteImpl. +/// +/// For Array, ChunkedArray, and Scalar Datum kinds, may rely on the execution +/// of concrete Function types, but must handle other Datum kinds on its own. +class ARROW_EXPORT MetaFunction : public Function { + public: + int num_kernels() const override { return 0; } + + Result<Datum> Execute(const std::vector<Datum>& args, const FunctionOptions* options, + ExecContext* ctx) const override; + + protected: + virtual Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const = 0; + MetaFunction(std::string name, const Arity& arity, const FunctionDoc* doc, - const FunctionOptions* default_options = NULLPTR) + const FunctionOptions* default_options = NULLPTR) : Function(std::move(name), Function::META, arity, doc, default_options) {} -}; - -/// @} - -} // namespace compute -} // namespace arrow +}; + +/// @} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.cc index 6d6dbb5ee5..f131f524d2 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.cc @@ -1,72 +1,72 @@ -// 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. - -#include "arrow/compute/kernel.h" - -#include <cstddef> -#include <memory> -#include <sstream> -#include <string> - -#include "arrow/buffer.h" -#include "arrow/compute/exec.h" -#include "arrow/compute/util_internal.h" -#include "arrow/result.h" -#include "arrow/type_traits.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/checked_cast.h" -#include "arrow/util/hash_util.h" -#include "arrow/util/logging.h" -#include "arrow/util/macros.h" - -namespace arrow { - -using internal::checked_cast; -using internal::hash_combine; - -static constexpr size_t kHashSeed = 0; - -namespace compute { - -// ---------------------------------------------------------------------- -// KernelContext - -Result<std::shared_ptr<ResizableBuffer>> KernelContext::Allocate(int64_t nbytes) { - return AllocateResizableBuffer(nbytes, exec_ctx_->memory_pool()); -} - -Result<std::shared_ptr<ResizableBuffer>> KernelContext::AllocateBitmap(int64_t num_bits) { - const int64_t nbytes = BitUtil::BytesForBits(num_bits); - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<ResizableBuffer> result, - AllocateResizableBuffer(nbytes, exec_ctx_->memory_pool())); - // Since bitmaps are typically written bit by bit, we could leak uninitialized bits. - // Make sure all memory is initialized (this also appeases Valgrind). - internal::ZeroMemory(result.get()); - return result; -} - +// 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. + +#include "arrow/compute/kernel.h" + +#include <cstddef> +#include <memory> +#include <sstream> +#include <string> + +#include "arrow/buffer.h" +#include "arrow/compute/exec.h" +#include "arrow/compute/util_internal.h" +#include "arrow/result.h" +#include "arrow/type_traits.h" +#include "arrow/util/bit_util.h" +#include "arrow/util/checked_cast.h" +#include "arrow/util/hash_util.h" +#include "arrow/util/logging.h" +#include "arrow/util/macros.h" + +namespace arrow { + +using internal::checked_cast; +using internal::hash_combine; + +static constexpr size_t kHashSeed = 0; + +namespace compute { + +// ---------------------------------------------------------------------- +// KernelContext + +Result<std::shared_ptr<ResizableBuffer>> KernelContext::Allocate(int64_t nbytes) { + return AllocateResizableBuffer(nbytes, exec_ctx_->memory_pool()); +} + +Result<std::shared_ptr<ResizableBuffer>> KernelContext::AllocateBitmap(int64_t num_bits) { + const int64_t nbytes = BitUtil::BytesForBits(num_bits); + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<ResizableBuffer> result, + AllocateResizableBuffer(nbytes, exec_ctx_->memory_pool())); + // Since bitmaps are typically written bit by bit, we could leak uninitialized bits. + // Make sure all memory is initialized (this also appeases Valgrind). + internal::ZeroMemory(result.get()); + return result; +} + Status Kernel::InitAll(KernelContext* ctx, const KernelInitArgs& args, std::vector<std::unique_ptr<KernelState>>* states) { for (auto& state : *states) { ARROW_ASSIGN_OR_RAISE(state, args.kernel->init(ctx, args)); - } + } return Status::OK(); -} - +} + Result<std::unique_ptr<KernelState>> ScalarAggregateKernel::MergeAll( const ScalarAggregateKernel* kernel, KernelContext* ctx, std::vector<std::unique_ptr<KernelState>> states) { @@ -78,409 +78,409 @@ Result<std::unique_ptr<KernelState>> ScalarAggregateKernel::MergeAll( } return std::move(out); } - -// ---------------------------------------------------------------------- -// Some basic TypeMatcher implementations - -namespace match { - -class SameTypeIdMatcher : public TypeMatcher { - public: - explicit SameTypeIdMatcher(Type::type accepted_id) : accepted_id_(accepted_id) {} - - bool Matches(const DataType& type) const override { return type.id() == accepted_id_; } - - std::string ToString() const override { - std::stringstream ss; - ss << "Type::" << ::arrow::internal::ToString(accepted_id_); - return ss.str(); - } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const SameTypeIdMatcher*>(&other); - if (casted == nullptr) { - return false; - } - return this->accepted_id_ == casted->accepted_id_; - } - - private: - Type::type accepted_id_; -}; - -std::shared_ptr<TypeMatcher> SameTypeId(Type::type type_id) { - return std::make_shared<SameTypeIdMatcher>(type_id); -} - -template <typename ArrowType> -class TimeUnitMatcher : public TypeMatcher { - using ThisType = TimeUnitMatcher<ArrowType>; - - public: - explicit TimeUnitMatcher(TimeUnit::type accepted_unit) - : accepted_unit_(accepted_unit) {} - - bool Matches(const DataType& type) const override { - if (type.id() != ArrowType::type_id) { - return false; - } - const auto& time_type = checked_cast<const ArrowType&>(type); - return time_type.unit() == accepted_unit_; - } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const ThisType*>(&other); - if (casted == nullptr) { - return false; - } - return this->accepted_unit_ == casted->accepted_unit_; - } - - std::string ToString() const override { - std::stringstream ss; - ss << ArrowType::type_name() << "(" << ::arrow::internal::ToString(accepted_unit_) - << ")"; - return ss.str(); - } - - private: - TimeUnit::type accepted_unit_; -}; - -using DurationTypeUnitMatcher = TimeUnitMatcher<DurationType>; -using Time32TypeUnitMatcher = TimeUnitMatcher<Time32Type>; -using Time64TypeUnitMatcher = TimeUnitMatcher<Time64Type>; -using TimestampTypeUnitMatcher = TimeUnitMatcher<TimestampType>; - -std::shared_ptr<TypeMatcher> TimestampTypeUnit(TimeUnit::type unit) { - return std::make_shared<TimestampTypeUnitMatcher>(unit); -} - -std::shared_ptr<TypeMatcher> Time32TypeUnit(TimeUnit::type unit) { - return std::make_shared<Time32TypeUnitMatcher>(unit); -} - -std::shared_ptr<TypeMatcher> Time64TypeUnit(TimeUnit::type unit) { - return std::make_shared<Time64TypeUnitMatcher>(unit); -} - -std::shared_ptr<TypeMatcher> DurationTypeUnit(TimeUnit::type unit) { - return std::make_shared<DurationTypeUnitMatcher>(unit); -} - -class IntegerMatcher : public TypeMatcher { - public: - IntegerMatcher() {} - - bool Matches(const DataType& type) const override { return is_integer(type.id()); } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const IntegerMatcher*>(&other); - return casted != nullptr; - } - - std::string ToString() const override { return "integer"; } -}; - -std::shared_ptr<TypeMatcher> Integer() { return std::make_shared<IntegerMatcher>(); } - -class PrimitiveMatcher : public TypeMatcher { - public: - PrimitiveMatcher() {} - - bool Matches(const DataType& type) const override { return is_primitive(type.id()); } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const PrimitiveMatcher*>(&other); - return casted != nullptr; - } - - std::string ToString() const override { return "primitive"; } -}; - -std::shared_ptr<TypeMatcher> Primitive() { return std::make_shared<PrimitiveMatcher>(); } - -class BinaryLikeMatcher : public TypeMatcher { - public: - BinaryLikeMatcher() {} - - bool Matches(const DataType& type) const override { return is_binary_like(type.id()); } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const BinaryLikeMatcher*>(&other); - return casted != nullptr; - } - std::string ToString() const override { return "binary-like"; } -}; - -std::shared_ptr<TypeMatcher> BinaryLike() { - return std::make_shared<BinaryLikeMatcher>(); -} - -class LargeBinaryLikeMatcher : public TypeMatcher { - public: - LargeBinaryLikeMatcher() {} - - bool Matches(const DataType& type) const override { - return is_large_binary_like(type.id()); - } - - bool Equals(const TypeMatcher& other) const override { - if (this == &other) { - return true; - } - auto casted = dynamic_cast<const LargeBinaryLikeMatcher*>(&other); - return casted != nullptr; - } - std::string ToString() const override { return "large-binary-like"; } -}; - -std::shared_ptr<TypeMatcher> LargeBinaryLike() { - return std::make_shared<LargeBinaryLikeMatcher>(); -} - -} // namespace match - -// ---------------------------------------------------------------------- -// InputType - -size_t InputType::Hash() const { - size_t result = kHashSeed; - hash_combine(result, static_cast<int>(shape_)); - hash_combine(result, static_cast<int>(kind_)); - switch (kind_) { - case InputType::EXACT_TYPE: - hash_combine(result, type_->Hash()); - break; - default: - break; - } - return result; -} - -std::string InputType::ToString() const { - std::stringstream ss; - switch (shape_) { - case ValueDescr::ANY: - ss << "any"; - break; - case ValueDescr::ARRAY: - ss << "array"; - break; - case ValueDescr::SCALAR: - ss << "scalar"; - break; - default: - DCHECK(false); - break; - } - ss << "["; - switch (kind_) { - case InputType::ANY_TYPE: - ss << "any"; - break; - case InputType::EXACT_TYPE: - ss << type_->ToString(); - break; - case InputType::USE_TYPE_MATCHER: { - ss << type_matcher_->ToString(); - } break; - default: - DCHECK(false); - break; - } - ss << "]"; - return ss.str(); -} - -bool InputType::Equals(const InputType& other) const { - if (this == &other) { - return true; - } - if (kind_ != other.kind_ || shape_ != other.shape_) { - return false; - } - switch (kind_) { - case InputType::ANY_TYPE: - return true; - case InputType::EXACT_TYPE: - return type_->Equals(*other.type_); - case InputType::USE_TYPE_MATCHER: - return type_matcher_->Equals(*other.type_matcher_); - default: - return false; - } -} - -bool InputType::Matches(const ValueDescr& descr) const { - if (shape_ != ValueDescr::ANY && descr.shape != shape_) { - return false; - } - switch (kind_) { - case InputType::EXACT_TYPE: - return type_->Equals(*descr.type); - case InputType::USE_TYPE_MATCHER: - return type_matcher_->Matches(*descr.type); - default: - // ANY_TYPE - return true; - } -} - -bool InputType::Matches(const Datum& value) const { return Matches(value.descr()); } - -const std::shared_ptr<DataType>& InputType::type() const { - DCHECK_EQ(InputType::EXACT_TYPE, kind_); - return type_; -} - -const TypeMatcher& InputType::type_matcher() const { - DCHECK_EQ(InputType::USE_TYPE_MATCHER, kind_); - return *type_matcher_; -} - -// ---------------------------------------------------------------------- -// OutputType - -OutputType::OutputType(ValueDescr descr) : OutputType(descr.type) { - shape_ = descr.shape; -} - -Result<ValueDescr> OutputType::Resolve(KernelContext* ctx, - const std::vector<ValueDescr>& args) const { - ValueDescr::Shape broadcasted_shape = GetBroadcastShape(args); - if (kind_ == OutputType::FIXED) { - return ValueDescr(type_, shape_ == ValueDescr::ANY ? broadcasted_shape : shape_); - } else { - ARROW_ASSIGN_OR_RAISE(ValueDescr resolved_descr, resolver_(ctx, args)); - if (resolved_descr.shape == ValueDescr::ANY) { - resolved_descr.shape = broadcasted_shape; - } - return resolved_descr; - } -} - -const std::shared_ptr<DataType>& OutputType::type() const { - DCHECK_EQ(FIXED, kind_); - return type_; -} - -const OutputType::Resolver& OutputType::resolver() const { - DCHECK_EQ(COMPUTED, kind_); - return resolver_; -} - -std::string OutputType::ToString() const { - if (kind_ == OutputType::FIXED) { - return type_->ToString(); - } else { - return "computed"; - } -} - -// ---------------------------------------------------------------------- -// KernelSignature - -KernelSignature::KernelSignature(std::vector<InputType> in_types, OutputType out_type, - bool is_varargs) - : in_types_(std::move(in_types)), - out_type_(std::move(out_type)), - is_varargs_(is_varargs), - hash_code_(0) { + +// ---------------------------------------------------------------------- +// Some basic TypeMatcher implementations + +namespace match { + +class SameTypeIdMatcher : public TypeMatcher { + public: + explicit SameTypeIdMatcher(Type::type accepted_id) : accepted_id_(accepted_id) {} + + bool Matches(const DataType& type) const override { return type.id() == accepted_id_; } + + std::string ToString() const override { + std::stringstream ss; + ss << "Type::" << ::arrow::internal::ToString(accepted_id_); + return ss.str(); + } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const SameTypeIdMatcher*>(&other); + if (casted == nullptr) { + return false; + } + return this->accepted_id_ == casted->accepted_id_; + } + + private: + Type::type accepted_id_; +}; + +std::shared_ptr<TypeMatcher> SameTypeId(Type::type type_id) { + return std::make_shared<SameTypeIdMatcher>(type_id); +} + +template <typename ArrowType> +class TimeUnitMatcher : public TypeMatcher { + using ThisType = TimeUnitMatcher<ArrowType>; + + public: + explicit TimeUnitMatcher(TimeUnit::type accepted_unit) + : accepted_unit_(accepted_unit) {} + + bool Matches(const DataType& type) const override { + if (type.id() != ArrowType::type_id) { + return false; + } + const auto& time_type = checked_cast<const ArrowType&>(type); + return time_type.unit() == accepted_unit_; + } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const ThisType*>(&other); + if (casted == nullptr) { + return false; + } + return this->accepted_unit_ == casted->accepted_unit_; + } + + std::string ToString() const override { + std::stringstream ss; + ss << ArrowType::type_name() << "(" << ::arrow::internal::ToString(accepted_unit_) + << ")"; + return ss.str(); + } + + private: + TimeUnit::type accepted_unit_; +}; + +using DurationTypeUnitMatcher = TimeUnitMatcher<DurationType>; +using Time32TypeUnitMatcher = TimeUnitMatcher<Time32Type>; +using Time64TypeUnitMatcher = TimeUnitMatcher<Time64Type>; +using TimestampTypeUnitMatcher = TimeUnitMatcher<TimestampType>; + +std::shared_ptr<TypeMatcher> TimestampTypeUnit(TimeUnit::type unit) { + return std::make_shared<TimestampTypeUnitMatcher>(unit); +} + +std::shared_ptr<TypeMatcher> Time32TypeUnit(TimeUnit::type unit) { + return std::make_shared<Time32TypeUnitMatcher>(unit); +} + +std::shared_ptr<TypeMatcher> Time64TypeUnit(TimeUnit::type unit) { + return std::make_shared<Time64TypeUnitMatcher>(unit); +} + +std::shared_ptr<TypeMatcher> DurationTypeUnit(TimeUnit::type unit) { + return std::make_shared<DurationTypeUnitMatcher>(unit); +} + +class IntegerMatcher : public TypeMatcher { + public: + IntegerMatcher() {} + + bool Matches(const DataType& type) const override { return is_integer(type.id()); } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const IntegerMatcher*>(&other); + return casted != nullptr; + } + + std::string ToString() const override { return "integer"; } +}; + +std::shared_ptr<TypeMatcher> Integer() { return std::make_shared<IntegerMatcher>(); } + +class PrimitiveMatcher : public TypeMatcher { + public: + PrimitiveMatcher() {} + + bool Matches(const DataType& type) const override { return is_primitive(type.id()); } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const PrimitiveMatcher*>(&other); + return casted != nullptr; + } + + std::string ToString() const override { return "primitive"; } +}; + +std::shared_ptr<TypeMatcher> Primitive() { return std::make_shared<PrimitiveMatcher>(); } + +class BinaryLikeMatcher : public TypeMatcher { + public: + BinaryLikeMatcher() {} + + bool Matches(const DataType& type) const override { return is_binary_like(type.id()); } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const BinaryLikeMatcher*>(&other); + return casted != nullptr; + } + std::string ToString() const override { return "binary-like"; } +}; + +std::shared_ptr<TypeMatcher> BinaryLike() { + return std::make_shared<BinaryLikeMatcher>(); +} + +class LargeBinaryLikeMatcher : public TypeMatcher { + public: + LargeBinaryLikeMatcher() {} + + bool Matches(const DataType& type) const override { + return is_large_binary_like(type.id()); + } + + bool Equals(const TypeMatcher& other) const override { + if (this == &other) { + return true; + } + auto casted = dynamic_cast<const LargeBinaryLikeMatcher*>(&other); + return casted != nullptr; + } + std::string ToString() const override { return "large-binary-like"; } +}; + +std::shared_ptr<TypeMatcher> LargeBinaryLike() { + return std::make_shared<LargeBinaryLikeMatcher>(); +} + +} // namespace match + +// ---------------------------------------------------------------------- +// InputType + +size_t InputType::Hash() const { + size_t result = kHashSeed; + hash_combine(result, static_cast<int>(shape_)); + hash_combine(result, static_cast<int>(kind_)); + switch (kind_) { + case InputType::EXACT_TYPE: + hash_combine(result, type_->Hash()); + break; + default: + break; + } + return result; +} + +std::string InputType::ToString() const { + std::stringstream ss; + switch (shape_) { + case ValueDescr::ANY: + ss << "any"; + break; + case ValueDescr::ARRAY: + ss << "array"; + break; + case ValueDescr::SCALAR: + ss << "scalar"; + break; + default: + DCHECK(false); + break; + } + ss << "["; + switch (kind_) { + case InputType::ANY_TYPE: + ss << "any"; + break; + case InputType::EXACT_TYPE: + ss << type_->ToString(); + break; + case InputType::USE_TYPE_MATCHER: { + ss << type_matcher_->ToString(); + } break; + default: + DCHECK(false); + break; + } + ss << "]"; + return ss.str(); +} + +bool InputType::Equals(const InputType& other) const { + if (this == &other) { + return true; + } + if (kind_ != other.kind_ || shape_ != other.shape_) { + return false; + } + switch (kind_) { + case InputType::ANY_TYPE: + return true; + case InputType::EXACT_TYPE: + return type_->Equals(*other.type_); + case InputType::USE_TYPE_MATCHER: + return type_matcher_->Equals(*other.type_matcher_); + default: + return false; + } +} + +bool InputType::Matches(const ValueDescr& descr) const { + if (shape_ != ValueDescr::ANY && descr.shape != shape_) { + return false; + } + switch (kind_) { + case InputType::EXACT_TYPE: + return type_->Equals(*descr.type); + case InputType::USE_TYPE_MATCHER: + return type_matcher_->Matches(*descr.type); + default: + // ANY_TYPE + return true; + } +} + +bool InputType::Matches(const Datum& value) const { return Matches(value.descr()); } + +const std::shared_ptr<DataType>& InputType::type() const { + DCHECK_EQ(InputType::EXACT_TYPE, kind_); + return type_; +} + +const TypeMatcher& InputType::type_matcher() const { + DCHECK_EQ(InputType::USE_TYPE_MATCHER, kind_); + return *type_matcher_; +} + +// ---------------------------------------------------------------------- +// OutputType + +OutputType::OutputType(ValueDescr descr) : OutputType(descr.type) { + shape_ = descr.shape; +} + +Result<ValueDescr> OutputType::Resolve(KernelContext* ctx, + const std::vector<ValueDescr>& args) const { + ValueDescr::Shape broadcasted_shape = GetBroadcastShape(args); + if (kind_ == OutputType::FIXED) { + return ValueDescr(type_, shape_ == ValueDescr::ANY ? broadcasted_shape : shape_); + } else { + ARROW_ASSIGN_OR_RAISE(ValueDescr resolved_descr, resolver_(ctx, args)); + if (resolved_descr.shape == ValueDescr::ANY) { + resolved_descr.shape = broadcasted_shape; + } + return resolved_descr; + } +} + +const std::shared_ptr<DataType>& OutputType::type() const { + DCHECK_EQ(FIXED, kind_); + return type_; +} + +const OutputType::Resolver& OutputType::resolver() const { + DCHECK_EQ(COMPUTED, kind_); + return resolver_; +} + +std::string OutputType::ToString() const { + if (kind_ == OutputType::FIXED) { + return type_->ToString(); + } else { + return "computed"; + } +} + +// ---------------------------------------------------------------------- +// KernelSignature + +KernelSignature::KernelSignature(std::vector<InputType> in_types, OutputType out_type, + bool is_varargs) + : in_types_(std::move(in_types)), + out_type_(std::move(out_type)), + is_varargs_(is_varargs), + hash_code_(0) { DCHECK(!is_varargs || (is_varargs && (in_types_.size() >= 1))); -} - -std::shared_ptr<KernelSignature> KernelSignature::Make(std::vector<InputType> in_types, - OutputType out_type, - bool is_varargs) { - return std::make_shared<KernelSignature>(std::move(in_types), std::move(out_type), - is_varargs); -} - -bool KernelSignature::Equals(const KernelSignature& other) const { - if (is_varargs_ != other.is_varargs_) { - return false; - } - if (in_types_.size() != other.in_types_.size()) { - return false; - } - for (size_t i = 0; i < in_types_.size(); ++i) { - if (!in_types_[i].Equals(other.in_types_[i])) { - return false; - } - } - return true; -} - -bool KernelSignature::MatchesInputs(const std::vector<ValueDescr>& args) const { - if (is_varargs_) { +} + +std::shared_ptr<KernelSignature> KernelSignature::Make(std::vector<InputType> in_types, + OutputType out_type, + bool is_varargs) { + return std::make_shared<KernelSignature>(std::move(in_types), std::move(out_type), + is_varargs); +} + +bool KernelSignature::Equals(const KernelSignature& other) const { + if (is_varargs_ != other.is_varargs_) { + return false; + } + if (in_types_.size() != other.in_types_.size()) { + return false; + } + for (size_t i = 0; i < in_types_.size(); ++i) { + if (!in_types_[i].Equals(other.in_types_[i])) { + return false; + } + } + return true; +} + +bool KernelSignature::MatchesInputs(const std::vector<ValueDescr>& args) const { + if (is_varargs_) { for (size_t i = 0; i < args.size(); ++i) { if (!in_types_[std::min(i, in_types_.size() - 1)].Matches(args[i])) { - return false; - } - } - } else { - if (args.size() != in_types_.size()) { - return false; - } - for (size_t i = 0; i < in_types_.size(); ++i) { - if (!in_types_[i].Matches(args[i])) { - return false; - } - } - } - return true; -} - -size_t KernelSignature::Hash() const { - if (hash_code_ != 0) { - return hash_code_; - } - size_t result = kHashSeed; - for (const auto& in_type : in_types_) { - hash_combine(result, in_type.Hash()); - } - hash_code_ = result; - return result; -} - -std::string KernelSignature::ToString() const { - std::stringstream ss; - - if (is_varargs_) { + return false; + } + } + } else { + if (args.size() != in_types_.size()) { + return false; + } + for (size_t i = 0; i < in_types_.size(); ++i) { + if (!in_types_[i].Matches(args[i])) { + return false; + } + } + } + return true; +} + +size_t KernelSignature::Hash() const { + if (hash_code_ != 0) { + return hash_code_; + } + size_t result = kHashSeed; + for (const auto& in_type : in_types_) { + hash_combine(result, in_type.Hash()); + } + hash_code_ = result; + return result; +} + +std::string KernelSignature::ToString() const { + std::stringstream ss; + + if (is_varargs_) { ss << "varargs["; - } else { - ss << "("; + } else { + ss << "("; } for (size_t i = 0; i < in_types_.size(); ++i) { if (i > 0) { ss << ", "; - } + } ss << in_types_[i].ToString(); } if (is_varargs_) { ss << "]"; } else { - ss << ")"; - } - ss << " -> " << out_type_.ToString(); - return ss.str(); -} - -} // namespace compute -} // namespace arrow + ss << ")"; + } + ss << " -> " << out_type_.ToString(); + return ss.str(); +} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.h index 6cea5558e9..36d20c7289 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernel.h @@ -1,695 +1,695 @@ -// 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. - -// NOTE: API is EXPERIMENTAL and will change without going through a -// deprecation cycle - -#pragma once - -#include <cstddef> -#include <cstdint> -#include <functional> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "arrow/buffer.h" -#include "arrow/compute/exec.h" -#include "arrow/datum.h" -#include "arrow/memory_pool.h" -#include "arrow/result.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace compute { - +// 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. + +// NOTE: API is EXPERIMENTAL and will change without going through a +// deprecation cycle + +#pragma once + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "arrow/buffer.h" +#include "arrow/compute/exec.h" +#include "arrow/datum.h" +#include "arrow/memory_pool.h" +#include "arrow/result.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace compute { + class FunctionOptions; - -/// \brief Base class for opaque kernel-specific state. For example, if there -/// is some kind of initialization required. -struct ARROW_EXPORT KernelState { - virtual ~KernelState() = default; -}; - -/// \brief Context/state for the execution of a particular kernel. -class ARROW_EXPORT KernelContext { - public: + +/// \brief Base class for opaque kernel-specific state. For example, if there +/// is some kind of initialization required. +struct ARROW_EXPORT KernelState { + virtual ~KernelState() = default; +}; + +/// \brief Context/state for the execution of a particular kernel. +class ARROW_EXPORT KernelContext { + public: explicit KernelContext(ExecContext* exec_ctx) : exec_ctx_(exec_ctx), state_() {} - - /// \brief Allocate buffer from the context's memory pool. The contents are - /// not initialized. - Result<std::shared_ptr<ResizableBuffer>> Allocate(int64_t nbytes); - - /// \brief Allocate buffer for bitmap from the context's memory pool. Like - /// Allocate, the contents of the buffer are not initialized but the last - /// byte is preemptively zeroed to help avoid ASAN or valgrind issues. - Result<std::shared_ptr<ResizableBuffer>> AllocateBitmap(int64_t num_bits); - - /// \brief Assign the active KernelState to be utilized for each stage of - /// kernel execution. Ownership and memory lifetime of the KernelState must - /// be minded separately. - void SetState(KernelState* state) { state_ = state; } - - KernelState* state() { return state_; } - - /// \brief Configuration related to function execution that is to be shared - /// across multiple kernels. - ExecContext* exec_context() { return exec_ctx_; } - - /// \brief The memory pool to use for allocations. For now, it uses the - /// MemoryPool contained in the ExecContext used to create the KernelContext. - MemoryPool* memory_pool() { return exec_ctx_->memory_pool(); } - - private: - ExecContext* exec_ctx_; - KernelState* state_; -}; - -/// \brief The standard kernel execution API that must be implemented for -/// SCALAR and VECTOR kernel types. This includes both stateless and stateful -/// kernels. Kernels depending on some execution state access that state via -/// subclasses of KernelState set on the KernelContext object. May be used for -/// SCALAR and VECTOR kernel kinds. Implementations should endeavor to write -/// into pre-allocated memory if they are able, though for some kernels -/// (e.g. in cases when a builder like StringBuilder) must be employed this may -/// not be possible. + + /// \brief Allocate buffer from the context's memory pool. The contents are + /// not initialized. + Result<std::shared_ptr<ResizableBuffer>> Allocate(int64_t nbytes); + + /// \brief Allocate buffer for bitmap from the context's memory pool. Like + /// Allocate, the contents of the buffer are not initialized but the last + /// byte is preemptively zeroed to help avoid ASAN or valgrind issues. + Result<std::shared_ptr<ResizableBuffer>> AllocateBitmap(int64_t num_bits); + + /// \brief Assign the active KernelState to be utilized for each stage of + /// kernel execution. Ownership and memory lifetime of the KernelState must + /// be minded separately. + void SetState(KernelState* state) { state_ = state; } + + KernelState* state() { return state_; } + + /// \brief Configuration related to function execution that is to be shared + /// across multiple kernels. + ExecContext* exec_context() { return exec_ctx_; } + + /// \brief The memory pool to use for allocations. For now, it uses the + /// MemoryPool contained in the ExecContext used to create the KernelContext. + MemoryPool* memory_pool() { return exec_ctx_->memory_pool(); } + + private: + ExecContext* exec_ctx_; + KernelState* state_; +}; + +/// \brief The standard kernel execution API that must be implemented for +/// SCALAR and VECTOR kernel types. This includes both stateless and stateful +/// kernels. Kernels depending on some execution state access that state via +/// subclasses of KernelState set on the KernelContext object. May be used for +/// SCALAR and VECTOR kernel kinds. Implementations should endeavor to write +/// into pre-allocated memory if they are able, though for some kernels +/// (e.g. in cases when a builder like StringBuilder) must be employed this may +/// not be possible. using ArrayKernelExec = std::function<Status(KernelContext*, const ExecBatch&, Datum*)>; - -/// \brief An type-checking interface to permit customizable validation rules -/// for use with InputType and KernelSignature. This is for scenarios where the -/// acceptance is not an exact type instance, such as a TIMESTAMP type for a -/// specific TimeUnit, but permitting any time zone. -struct ARROW_EXPORT TypeMatcher { - virtual ~TypeMatcher() = default; - - /// \brief Return true if this matcher accepts the data type. - virtual bool Matches(const DataType& type) const = 0; - - /// \brief A human-interpretable string representation of what the type - /// matcher checks for, usable when printing KernelSignature or formatting - /// error messages. - virtual std::string ToString() const = 0; - - /// \brief Return true if this TypeMatcher contains the same matching rule as - /// the other. Currently depends on RTTI. - virtual bool Equals(const TypeMatcher& other) const = 0; -}; - -namespace match { - -/// \brief Match any DataType instance having the same DataType::id. -ARROW_EXPORT std::shared_ptr<TypeMatcher> SameTypeId(Type::type type_id); - -/// \brief Match any TimestampType instance having the same unit, but the time -/// zones can be different. -ARROW_EXPORT std::shared_ptr<TypeMatcher> TimestampTypeUnit(TimeUnit::type unit); -ARROW_EXPORT std::shared_ptr<TypeMatcher> Time32TypeUnit(TimeUnit::type unit); -ARROW_EXPORT std::shared_ptr<TypeMatcher> Time64TypeUnit(TimeUnit::type unit); -ARROW_EXPORT std::shared_ptr<TypeMatcher> DurationTypeUnit(TimeUnit::type unit); - -// \brief Match any integer type -ARROW_EXPORT std::shared_ptr<TypeMatcher> Integer(); - -// Match types using 32-bit varbinary representation -ARROW_EXPORT std::shared_ptr<TypeMatcher> BinaryLike(); - -// Match types using 64-bit varbinary representation -ARROW_EXPORT std::shared_ptr<TypeMatcher> LargeBinaryLike(); - -// \brief Match any primitive type (boolean or any type representable as a C -// Type) -ARROW_EXPORT std::shared_ptr<TypeMatcher> Primitive(); - -} // namespace match - -/// \brief An object used for type- and shape-checking arguments to be passed -/// to a kernel and stored in a KernelSignature. Distinguishes between ARRAY -/// and SCALAR arguments using ValueDescr::Shape. The type-checking rule can be -/// supplied either with an exact DataType instance or a custom TypeMatcher. -class ARROW_EXPORT InputType { - public: - /// \brief The kind of type-checking rule that the InputType contains. - enum Kind { - /// \brief Accept any value type. - ANY_TYPE, - - /// \brief A fixed arrow::DataType and will only exact match having this - /// exact type (e.g. same TimestampType unit, same decimal scale and - /// precision, or same nested child types). - EXACT_TYPE, - - /// \brief Uses a TypeMatcher implementation to check the type. - USE_TYPE_MATCHER - }; - - /// \brief Accept any value type but with a specific shape (e.g. any Array or - /// any Scalar). - InputType(ValueDescr::Shape shape = ValueDescr::ANY) // NOLINT implicit construction - : kind_(ANY_TYPE), shape_(shape) {} - - /// \brief Accept an exact value type. - InputType(std::shared_ptr<DataType> type, // NOLINT implicit construction - ValueDescr::Shape shape = ValueDescr::ANY) - : kind_(EXACT_TYPE), shape_(shape), type_(std::move(type)) {} - - /// \brief Accept an exact value type and shape provided by a ValueDescr. - InputType(const ValueDescr& descr) // NOLINT implicit construction - : InputType(descr.type, descr.shape) {} - - /// \brief Use the passed TypeMatcher to type check. - InputType(std::shared_ptr<TypeMatcher> type_matcher, // NOLINT implicit construction - ValueDescr::Shape shape = ValueDescr::ANY) - : kind_(USE_TYPE_MATCHER), shape_(shape), type_matcher_(std::move(type_matcher)) {} - - /// \brief Match any type with the given Type::type. Uses a TypeMatcher for - /// its implementation. - explicit InputType(Type::type type_id, ValueDescr::Shape shape = ValueDescr::ANY) - : InputType(match::SameTypeId(type_id), shape) {} - - InputType(const InputType& other) { CopyInto(other); } - - void operator=(const InputType& other) { CopyInto(other); } - - InputType(InputType&& other) { MoveInto(std::forward<InputType>(other)); } - - void operator=(InputType&& other) { MoveInto(std::forward<InputType>(other)); } - - // \brief Match an array with the given exact type. Convenience constructor. - static InputType Array(std::shared_ptr<DataType> type) { - return InputType(std::move(type), ValueDescr::ARRAY); - } - - // \brief Match a scalar with the given exact type. Convenience constructor. - static InputType Scalar(std::shared_ptr<DataType> type) { - return InputType(std::move(type), ValueDescr::SCALAR); - } - - // \brief Match an array with the given Type::type id. Convenience - // constructor. - static InputType Array(Type::type id) { return InputType(id, ValueDescr::ARRAY); } - - // \brief Match a scalar with the given Type::type id. Convenience - // constructor. - static InputType Scalar(Type::type id) { return InputType(id, ValueDescr::SCALAR); } - - /// \brief Return true if this input type matches the same type cases as the - /// other. - bool Equals(const InputType& other) const; - - bool operator==(const InputType& other) const { return this->Equals(other); } - - bool operator!=(const InputType& other) const { return !(*this == other); } - - /// \brief Return hash code. - size_t Hash() const; - - /// \brief Render a human-readable string representation. - std::string ToString() const; - - /// \brief Return true if the value matches this argument kind in type - /// and shape. - bool Matches(const Datum& value) const; - - /// \brief Return true if the value descriptor matches this argument kind in - /// type and shape. - bool Matches(const ValueDescr& value) const; - - /// \brief The type matching rule that this InputType uses. - Kind kind() const { return kind_; } - - /// \brief Indicates whether this InputType matches Array (ValueDescr::ARRAY), - /// Scalar (ValueDescr::SCALAR) values, or both (ValueDescr::ANY). - ValueDescr::Shape shape() const { return shape_; } - - /// \brief For InputType::EXACT_TYPE kind, the exact type that this InputType - /// must match. Otherwise this function should not be used and will assert in - /// debug builds. - const std::shared_ptr<DataType>& type() const; - - /// \brief For InputType::USE_TYPE_MATCHER, the TypeMatcher to be used for - /// checking the type of a value. Otherwise this function should not be used - /// and will assert in debug builds. - const TypeMatcher& type_matcher() const; - - private: - void CopyInto(const InputType& other) { - this->kind_ = other.kind_; - this->shape_ = other.shape_; - this->type_ = other.type_; - this->type_matcher_ = other.type_matcher_; - } - - void MoveInto(InputType&& other) { - this->kind_ = other.kind_; - this->shape_ = other.shape_; - this->type_ = std::move(other.type_); - this->type_matcher_ = std::move(other.type_matcher_); - } - - Kind kind_; - - ValueDescr::Shape shape_ = ValueDescr::ANY; - - // For EXACT_TYPE Kind - std::shared_ptr<DataType> type_; - - // For USE_TYPE_MATCHER Kind - std::shared_ptr<TypeMatcher> type_matcher_; -}; - -/// \brief Container to capture both exact and input-dependent output types. -/// -/// The value shape returned by Resolve will be determined by broadcasting the -/// shapes of the input arguments, otherwise this is handled by the -/// user-defined resolver function: -/// -/// * Any ARRAY shape -> output shape is ARRAY -/// * All SCALAR shapes -> output shape is SCALAR -class ARROW_EXPORT OutputType { - public: - /// \brief An enum indicating whether the value type is an invariant fixed - /// value or one that's computed by a kernel-defined resolver function. - enum ResolveKind { FIXED, COMPUTED }; - - /// Type resolution function. Given input types and shapes, return output - /// type and shape. This function SHOULD _not_ be used to check for arity, - /// that is to be performed one or more layers above. May make use of kernel - /// state to know what type to output in some cases. - using Resolver = - std::function<Result<ValueDescr>(KernelContext*, const std::vector<ValueDescr>&)>; - - /// \brief Output an exact type, but with shape determined by promoting the - /// shapes of the inputs (any ARRAY argument yields ARRAY). - OutputType(std::shared_ptr<DataType> type) // NOLINT implicit construction - : kind_(FIXED), type_(std::move(type)) {} - - /// \brief Output the exact type and shape provided by a ValueDescr - OutputType(ValueDescr descr); // NOLINT implicit construction - - explicit OutputType(Resolver resolver) - : kind_(COMPUTED), resolver_(std::move(resolver)) {} - - OutputType(const OutputType& other) { - this->kind_ = other.kind_; - this->shape_ = other.shape_; - this->type_ = other.type_; - this->resolver_ = other.resolver_; - } - - OutputType(OutputType&& other) { - this->kind_ = other.kind_; - this->type_ = std::move(other.type_); - this->shape_ = other.shape_; - this->resolver_ = other.resolver_; - } - + +/// \brief An type-checking interface to permit customizable validation rules +/// for use with InputType and KernelSignature. This is for scenarios where the +/// acceptance is not an exact type instance, such as a TIMESTAMP type for a +/// specific TimeUnit, but permitting any time zone. +struct ARROW_EXPORT TypeMatcher { + virtual ~TypeMatcher() = default; + + /// \brief Return true if this matcher accepts the data type. + virtual bool Matches(const DataType& type) const = 0; + + /// \brief A human-interpretable string representation of what the type + /// matcher checks for, usable when printing KernelSignature or formatting + /// error messages. + virtual std::string ToString() const = 0; + + /// \brief Return true if this TypeMatcher contains the same matching rule as + /// the other. Currently depends on RTTI. + virtual bool Equals(const TypeMatcher& other) const = 0; +}; + +namespace match { + +/// \brief Match any DataType instance having the same DataType::id. +ARROW_EXPORT std::shared_ptr<TypeMatcher> SameTypeId(Type::type type_id); + +/// \brief Match any TimestampType instance having the same unit, but the time +/// zones can be different. +ARROW_EXPORT std::shared_ptr<TypeMatcher> TimestampTypeUnit(TimeUnit::type unit); +ARROW_EXPORT std::shared_ptr<TypeMatcher> Time32TypeUnit(TimeUnit::type unit); +ARROW_EXPORT std::shared_ptr<TypeMatcher> Time64TypeUnit(TimeUnit::type unit); +ARROW_EXPORT std::shared_ptr<TypeMatcher> DurationTypeUnit(TimeUnit::type unit); + +// \brief Match any integer type +ARROW_EXPORT std::shared_ptr<TypeMatcher> Integer(); + +// Match types using 32-bit varbinary representation +ARROW_EXPORT std::shared_ptr<TypeMatcher> BinaryLike(); + +// Match types using 64-bit varbinary representation +ARROW_EXPORT std::shared_ptr<TypeMatcher> LargeBinaryLike(); + +// \brief Match any primitive type (boolean or any type representable as a C +// Type) +ARROW_EXPORT std::shared_ptr<TypeMatcher> Primitive(); + +} // namespace match + +/// \brief An object used for type- and shape-checking arguments to be passed +/// to a kernel and stored in a KernelSignature. Distinguishes between ARRAY +/// and SCALAR arguments using ValueDescr::Shape. The type-checking rule can be +/// supplied either with an exact DataType instance or a custom TypeMatcher. +class ARROW_EXPORT InputType { + public: + /// \brief The kind of type-checking rule that the InputType contains. + enum Kind { + /// \brief Accept any value type. + ANY_TYPE, + + /// \brief A fixed arrow::DataType and will only exact match having this + /// exact type (e.g. same TimestampType unit, same decimal scale and + /// precision, or same nested child types). + EXACT_TYPE, + + /// \brief Uses a TypeMatcher implementation to check the type. + USE_TYPE_MATCHER + }; + + /// \brief Accept any value type but with a specific shape (e.g. any Array or + /// any Scalar). + InputType(ValueDescr::Shape shape = ValueDescr::ANY) // NOLINT implicit construction + : kind_(ANY_TYPE), shape_(shape) {} + + /// \brief Accept an exact value type. + InputType(std::shared_ptr<DataType> type, // NOLINT implicit construction + ValueDescr::Shape shape = ValueDescr::ANY) + : kind_(EXACT_TYPE), shape_(shape), type_(std::move(type)) {} + + /// \brief Accept an exact value type and shape provided by a ValueDescr. + InputType(const ValueDescr& descr) // NOLINT implicit construction + : InputType(descr.type, descr.shape) {} + + /// \brief Use the passed TypeMatcher to type check. + InputType(std::shared_ptr<TypeMatcher> type_matcher, // NOLINT implicit construction + ValueDescr::Shape shape = ValueDescr::ANY) + : kind_(USE_TYPE_MATCHER), shape_(shape), type_matcher_(std::move(type_matcher)) {} + + /// \brief Match any type with the given Type::type. Uses a TypeMatcher for + /// its implementation. + explicit InputType(Type::type type_id, ValueDescr::Shape shape = ValueDescr::ANY) + : InputType(match::SameTypeId(type_id), shape) {} + + InputType(const InputType& other) { CopyInto(other); } + + void operator=(const InputType& other) { CopyInto(other); } + + InputType(InputType&& other) { MoveInto(std::forward<InputType>(other)); } + + void operator=(InputType&& other) { MoveInto(std::forward<InputType>(other)); } + + // \brief Match an array with the given exact type. Convenience constructor. + static InputType Array(std::shared_ptr<DataType> type) { + return InputType(std::move(type), ValueDescr::ARRAY); + } + + // \brief Match a scalar with the given exact type. Convenience constructor. + static InputType Scalar(std::shared_ptr<DataType> type) { + return InputType(std::move(type), ValueDescr::SCALAR); + } + + // \brief Match an array with the given Type::type id. Convenience + // constructor. + static InputType Array(Type::type id) { return InputType(id, ValueDescr::ARRAY); } + + // \brief Match a scalar with the given Type::type id. Convenience + // constructor. + static InputType Scalar(Type::type id) { return InputType(id, ValueDescr::SCALAR); } + + /// \brief Return true if this input type matches the same type cases as the + /// other. + bool Equals(const InputType& other) const; + + bool operator==(const InputType& other) const { return this->Equals(other); } + + bool operator!=(const InputType& other) const { return !(*this == other); } + + /// \brief Return hash code. + size_t Hash() const; + + /// \brief Render a human-readable string representation. + std::string ToString() const; + + /// \brief Return true if the value matches this argument kind in type + /// and shape. + bool Matches(const Datum& value) const; + + /// \brief Return true if the value descriptor matches this argument kind in + /// type and shape. + bool Matches(const ValueDescr& value) const; + + /// \brief The type matching rule that this InputType uses. + Kind kind() const { return kind_; } + + /// \brief Indicates whether this InputType matches Array (ValueDescr::ARRAY), + /// Scalar (ValueDescr::SCALAR) values, or both (ValueDescr::ANY). + ValueDescr::Shape shape() const { return shape_; } + + /// \brief For InputType::EXACT_TYPE kind, the exact type that this InputType + /// must match. Otherwise this function should not be used and will assert in + /// debug builds. + const std::shared_ptr<DataType>& type() const; + + /// \brief For InputType::USE_TYPE_MATCHER, the TypeMatcher to be used for + /// checking the type of a value. Otherwise this function should not be used + /// and will assert in debug builds. + const TypeMatcher& type_matcher() const; + + private: + void CopyInto(const InputType& other) { + this->kind_ = other.kind_; + this->shape_ = other.shape_; + this->type_ = other.type_; + this->type_matcher_ = other.type_matcher_; + } + + void MoveInto(InputType&& other) { + this->kind_ = other.kind_; + this->shape_ = other.shape_; + this->type_ = std::move(other.type_); + this->type_matcher_ = std::move(other.type_matcher_); + } + + Kind kind_; + + ValueDescr::Shape shape_ = ValueDescr::ANY; + + // For EXACT_TYPE Kind + std::shared_ptr<DataType> type_; + + // For USE_TYPE_MATCHER Kind + std::shared_ptr<TypeMatcher> type_matcher_; +}; + +/// \brief Container to capture both exact and input-dependent output types. +/// +/// The value shape returned by Resolve will be determined by broadcasting the +/// shapes of the input arguments, otherwise this is handled by the +/// user-defined resolver function: +/// +/// * Any ARRAY shape -> output shape is ARRAY +/// * All SCALAR shapes -> output shape is SCALAR +class ARROW_EXPORT OutputType { + public: + /// \brief An enum indicating whether the value type is an invariant fixed + /// value or one that's computed by a kernel-defined resolver function. + enum ResolveKind { FIXED, COMPUTED }; + + /// Type resolution function. Given input types and shapes, return output + /// type and shape. This function SHOULD _not_ be used to check for arity, + /// that is to be performed one or more layers above. May make use of kernel + /// state to know what type to output in some cases. + using Resolver = + std::function<Result<ValueDescr>(KernelContext*, const std::vector<ValueDescr>&)>; + + /// \brief Output an exact type, but with shape determined by promoting the + /// shapes of the inputs (any ARRAY argument yields ARRAY). + OutputType(std::shared_ptr<DataType> type) // NOLINT implicit construction + : kind_(FIXED), type_(std::move(type)) {} + + /// \brief Output the exact type and shape provided by a ValueDescr + OutputType(ValueDescr descr); // NOLINT implicit construction + + explicit OutputType(Resolver resolver) + : kind_(COMPUTED), resolver_(std::move(resolver)) {} + + OutputType(const OutputType& other) { + this->kind_ = other.kind_; + this->shape_ = other.shape_; + this->type_ = other.type_; + this->resolver_ = other.resolver_; + } + + OutputType(OutputType&& other) { + this->kind_ = other.kind_; + this->type_ = std::move(other.type_); + this->shape_ = other.shape_; + this->resolver_ = other.resolver_; + } + OutputType& operator=(const OutputType&) = default; OutputType& operator=(OutputType&&) = default; - /// \brief Return the shape and type of the expected output value of the - /// kernel given the value descriptors (shapes and types) of the input - /// arguments. The resolver may make use of state information kept in the - /// KernelContext. - Result<ValueDescr> Resolve(KernelContext* ctx, - const std::vector<ValueDescr>& args) const; - - /// \brief The exact output value type for the FIXED kind. - const std::shared_ptr<DataType>& type() const; - - /// \brief For use with COMPUTED resolution strategy. It may be more - /// convenient to invoke this with OutputType::Resolve returned from this - /// method. - const Resolver& resolver() const; - - /// \brief Render a human-readable string representation. - std::string ToString() const; - - /// \brief Return the kind of type resolution of this output type, whether - /// fixed/invariant or computed by a resolver. - ResolveKind kind() const { return kind_; } - - /// \brief If the shape is ANY, then Resolve will compute the shape based on - /// the input arguments. - ValueDescr::Shape shape() const { return shape_; } - - private: - ResolveKind kind_; - - // For FIXED resolution - std::shared_ptr<DataType> type_; - - /// \brief The shape of the output type to return when using Resolve. If ANY - /// will promote the input shapes. - ValueDescr::Shape shape_ = ValueDescr::ANY; - - // For COMPUTED resolution - Resolver resolver_; -}; - -/// \brief Holds the input types and output type of the kernel. -/// + /// \brief Return the shape and type of the expected output value of the + /// kernel given the value descriptors (shapes and types) of the input + /// arguments. The resolver may make use of state information kept in the + /// KernelContext. + Result<ValueDescr> Resolve(KernelContext* ctx, + const std::vector<ValueDescr>& args) const; + + /// \brief The exact output value type for the FIXED kind. + const std::shared_ptr<DataType>& type() const; + + /// \brief For use with COMPUTED resolution strategy. It may be more + /// convenient to invoke this with OutputType::Resolve returned from this + /// method. + const Resolver& resolver() const; + + /// \brief Render a human-readable string representation. + std::string ToString() const; + + /// \brief Return the kind of type resolution of this output type, whether + /// fixed/invariant or computed by a resolver. + ResolveKind kind() const { return kind_; } + + /// \brief If the shape is ANY, then Resolve will compute the shape based on + /// the input arguments. + ValueDescr::Shape shape() const { return shape_; } + + private: + ResolveKind kind_; + + // For FIXED resolution + std::shared_ptr<DataType> type_; + + /// \brief The shape of the output type to return when using Resolve. If ANY + /// will promote the input shapes. + ValueDescr::Shape shape_ = ValueDescr::ANY; + + // For COMPUTED resolution + Resolver resolver_; +}; + +/// \brief Holds the input types and output type of the kernel. +/// /// VarArgs functions with minimum N arguments should pass up to N input types to be /// used to validate the input types of a function invocation. The first N-1 types /// will be matched against the first N-1 arguments, and the last type will be /// matched against the remaining arguments. -class ARROW_EXPORT KernelSignature { - public: - KernelSignature(std::vector<InputType> in_types, OutputType out_type, - bool is_varargs = false); - - /// \brief Convenience ctor since make_shared can be awkward - static std::shared_ptr<KernelSignature> Make(std::vector<InputType> in_types, - OutputType out_type, - bool is_varargs = false); - - /// \brief Return true if the signature if compatible with the list of input - /// value descriptors. - bool MatchesInputs(const std::vector<ValueDescr>& descriptors) const; - - /// \brief Returns true if the input types of each signature are - /// equal. Well-formed functions should have a deterministic output type - /// given input types, but currently it is the responsibility of the - /// developer to ensure this. - bool Equals(const KernelSignature& other) const; - - bool operator==(const KernelSignature& other) const { return this->Equals(other); } - - bool operator!=(const KernelSignature& other) const { return !(*this == other); } - - /// \brief Compute a hash code for the signature - size_t Hash() const; - - /// \brief The input types for the kernel. For VarArgs functions, this should - /// generally contain a single validator to use for validating all of the - /// function arguments. - const std::vector<InputType>& in_types() const { return in_types_; } - - /// \brief The output type for the kernel. Use Resolve to return the exact - /// output given input argument ValueDescrs, since many kernels' output types - /// depend on their input types (or their type metadata). - const OutputType& out_type() const { return out_type_; } - - /// \brief Render a human-readable string representation - std::string ToString() const; - - bool is_varargs() const { return is_varargs_; } - - private: - std::vector<InputType> in_types_; - OutputType out_type_; - bool is_varargs_; - - // For caching the hash code after it's computed the first time - mutable uint64_t hash_code_; -}; - -/// \brief A function may contain multiple variants of a kernel for a given -/// type combination for different SIMD levels. Based on the active system's -/// CPU info or the user's preferences, we can elect to use one over the other. -struct SimdLevel { - enum type { NONE = 0, SSE4_2, AVX, AVX2, AVX512, NEON, MAX }; -}; - -/// \brief The strategy to use for propagating or otherwise populating the -/// validity bitmap of a kernel output. -struct NullHandling { - enum type { - /// Compute the output validity bitmap by intersecting the validity bitmaps - /// of the arguments using bitwise-and operations. This means that values - /// in the output are valid/non-null only if the corresponding values in - /// all input arguments were valid/non-null. Kernel generally need not - /// touch the bitmap thereafter, but a kernel's exec function is permitted - /// to alter the bitmap after the null intersection is computed if it needs - /// to. - INTERSECTION, - - /// Kernel expects a pre-allocated buffer to write the result bitmap - /// into. The preallocated memory is not zeroed (except for the last byte), - /// so the kernel should ensure to completely populate the bitmap. - COMPUTED_PREALLOCATE, - - /// Kernel allocates and sets the validity bitmap of the output. - COMPUTED_NO_PREALLOCATE, - - /// Kernel output is never null and a validity bitmap does not need to be - /// allocated. - OUTPUT_NOT_NULL - }; -}; - -/// \brief The preference for memory preallocation of fixed-width type outputs -/// in kernel execution. -struct MemAllocation { - enum type { - // For data types that support pre-allocation (i.e. fixed-width), the - // kernel expects to be provided a pre-allocated data buffer to write - // into. Non-fixed-width types must always allocate their own data - // buffers. The allocation made for the same length as the execution batch, - // so vector kernels yielding differently sized output should not use this. - // - // It is valid for the data to not be preallocated but the validity bitmap - // is (or is computed using the intersection/bitwise-and method). - // - // For variable-size output types like BinaryType or StringType, or for - // nested types, this option has no effect. - PREALLOCATE, - - // The kernel is responsible for allocating its own data buffer for - // fixed-width type outputs. - NO_PREALLOCATE - }; -}; - -struct Kernel; - -/// \brief Arguments to pass to a KernelInit function. A struct is used to help -/// avoid API breakage should the arguments passed need to be expanded. -struct KernelInitArgs { - /// \brief A pointer to the kernel being initialized. The init function may - /// depend on the kernel's KernelSignature or other data contained there. - const Kernel* kernel; - - /// \brief The types and shapes of the input arguments that the kernel is - /// about to be executed against. - /// - /// TODO: should this be const std::vector<ValueDescr>*? const-ref is being - /// used to avoid the cost of copying the struct into the args struct. - const std::vector<ValueDescr>& inputs; - - /// \brief Opaque options specific to this kernel. May be nullptr for functions - /// that do not require options. - const FunctionOptions* options; -}; - -/// \brief Common initializer function for all kernel types. +class ARROW_EXPORT KernelSignature { + public: + KernelSignature(std::vector<InputType> in_types, OutputType out_type, + bool is_varargs = false); + + /// \brief Convenience ctor since make_shared can be awkward + static std::shared_ptr<KernelSignature> Make(std::vector<InputType> in_types, + OutputType out_type, + bool is_varargs = false); + + /// \brief Return true if the signature if compatible with the list of input + /// value descriptors. + bool MatchesInputs(const std::vector<ValueDescr>& descriptors) const; + + /// \brief Returns true if the input types of each signature are + /// equal. Well-formed functions should have a deterministic output type + /// given input types, but currently it is the responsibility of the + /// developer to ensure this. + bool Equals(const KernelSignature& other) const; + + bool operator==(const KernelSignature& other) const { return this->Equals(other); } + + bool operator!=(const KernelSignature& other) const { return !(*this == other); } + + /// \brief Compute a hash code for the signature + size_t Hash() const; + + /// \brief The input types for the kernel. For VarArgs functions, this should + /// generally contain a single validator to use for validating all of the + /// function arguments. + const std::vector<InputType>& in_types() const { return in_types_; } + + /// \brief The output type for the kernel. Use Resolve to return the exact + /// output given input argument ValueDescrs, since many kernels' output types + /// depend on their input types (or their type metadata). + const OutputType& out_type() const { return out_type_; } + + /// \brief Render a human-readable string representation + std::string ToString() const; + + bool is_varargs() const { return is_varargs_; } + + private: + std::vector<InputType> in_types_; + OutputType out_type_; + bool is_varargs_; + + // For caching the hash code after it's computed the first time + mutable uint64_t hash_code_; +}; + +/// \brief A function may contain multiple variants of a kernel for a given +/// type combination for different SIMD levels. Based on the active system's +/// CPU info or the user's preferences, we can elect to use one over the other. +struct SimdLevel { + enum type { NONE = 0, SSE4_2, AVX, AVX2, AVX512, NEON, MAX }; +}; + +/// \brief The strategy to use for propagating or otherwise populating the +/// validity bitmap of a kernel output. +struct NullHandling { + enum type { + /// Compute the output validity bitmap by intersecting the validity bitmaps + /// of the arguments using bitwise-and operations. This means that values + /// in the output are valid/non-null only if the corresponding values in + /// all input arguments were valid/non-null. Kernel generally need not + /// touch the bitmap thereafter, but a kernel's exec function is permitted + /// to alter the bitmap after the null intersection is computed if it needs + /// to. + INTERSECTION, + + /// Kernel expects a pre-allocated buffer to write the result bitmap + /// into. The preallocated memory is not zeroed (except for the last byte), + /// so the kernel should ensure to completely populate the bitmap. + COMPUTED_PREALLOCATE, + + /// Kernel allocates and sets the validity bitmap of the output. + COMPUTED_NO_PREALLOCATE, + + /// Kernel output is never null and a validity bitmap does not need to be + /// allocated. + OUTPUT_NOT_NULL + }; +}; + +/// \brief The preference for memory preallocation of fixed-width type outputs +/// in kernel execution. +struct MemAllocation { + enum type { + // For data types that support pre-allocation (i.e. fixed-width), the + // kernel expects to be provided a pre-allocated data buffer to write + // into. Non-fixed-width types must always allocate their own data + // buffers. The allocation made for the same length as the execution batch, + // so vector kernels yielding differently sized output should not use this. + // + // It is valid for the data to not be preallocated but the validity bitmap + // is (or is computed using the intersection/bitwise-and method). + // + // For variable-size output types like BinaryType or StringType, or for + // nested types, this option has no effect. + PREALLOCATE, + + // The kernel is responsible for allocating its own data buffer for + // fixed-width type outputs. + NO_PREALLOCATE + }; +}; + +struct Kernel; + +/// \brief Arguments to pass to a KernelInit function. A struct is used to help +/// avoid API breakage should the arguments passed need to be expanded. +struct KernelInitArgs { + /// \brief A pointer to the kernel being initialized. The init function may + /// depend on the kernel's KernelSignature or other data contained there. + const Kernel* kernel; + + /// \brief The types and shapes of the input arguments that the kernel is + /// about to be executed against. + /// + /// TODO: should this be const std::vector<ValueDescr>*? const-ref is being + /// used to avoid the cost of copying the struct into the args struct. + const std::vector<ValueDescr>& inputs; + + /// \brief Opaque options specific to this kernel. May be nullptr for functions + /// that do not require options. + const FunctionOptions* options; +}; + +/// \brief Common initializer function for all kernel types. using KernelInit = std::function<Result<std::unique_ptr<KernelState>>( KernelContext*, const KernelInitArgs&)>; - -/// \brief Base type for kernels. Contains the function signature and -/// optionally the state initialization function, along with some common -/// attributes -struct Kernel { - Kernel() = default; - - Kernel(std::shared_ptr<KernelSignature> sig, KernelInit init) - : signature(std::move(sig)), init(std::move(init)) {} - - Kernel(std::vector<InputType> in_types, OutputType out_type, KernelInit init) + +/// \brief Base type for kernels. Contains the function signature and +/// optionally the state initialization function, along with some common +/// attributes +struct Kernel { + Kernel() = default; + + Kernel(std::shared_ptr<KernelSignature> sig, KernelInit init) + : signature(std::move(sig)), init(std::move(init)) {} + + Kernel(std::vector<InputType> in_types, OutputType out_type, KernelInit init) : Kernel(KernelSignature::Make(std::move(in_types), std::move(out_type)), std::move(init)) {} - - /// \brief The "signature" of the kernel containing the InputType input - /// argument validators and OutputType output type and shape resolver. - std::shared_ptr<KernelSignature> signature; - - /// \brief Create a new KernelState for invocations of this kernel, e.g. to - /// set up any options or state relevant for execution. - KernelInit init; - + + /// \brief The "signature" of the kernel containing the InputType input + /// argument validators and OutputType output type and shape resolver. + std::shared_ptr<KernelSignature> signature; + + /// \brief Create a new KernelState for invocations of this kernel, e.g. to + /// set up any options or state relevant for execution. + KernelInit init; + /// \brief Create a vector of new KernelState for invocations of this kernel. static Status InitAll(KernelContext*, const KernelInitArgs&, std::vector<std::unique_ptr<KernelState>>*); - /// \brief Indicates whether execution can benefit from parallelization - /// (splitting large chunks into smaller chunks and using multiple - /// threads). Some kernels may not support parallel execution at - /// all. Synchronization and concurrency-related issues are currently the - /// responsibility of the Kernel's implementation. - bool parallelizable = true; - - /// \brief Indicates the level of SIMD instruction support in the host CPU is - /// required to use the function. The intention is for functions to be able to - /// contain multiple kernels with the same signature but different levels of SIMD, - /// so that the most optimized kernel supported on a host's processor can be chosen. - SimdLevel::type simd_level = SimdLevel::NONE; -}; - -/// \brief Common kernel base data structure for ScalarKernel and -/// VectorKernel. It is called "ArrayKernel" in that the functions generally -/// output array values (as opposed to scalar values in the case of aggregate -/// functions). -struct ArrayKernel : public Kernel { + /// \brief Indicates whether execution can benefit from parallelization + /// (splitting large chunks into smaller chunks and using multiple + /// threads). Some kernels may not support parallel execution at + /// all. Synchronization and concurrency-related issues are currently the + /// responsibility of the Kernel's implementation. + bool parallelizable = true; + + /// \brief Indicates the level of SIMD instruction support in the host CPU is + /// required to use the function. The intention is for functions to be able to + /// contain multiple kernels with the same signature but different levels of SIMD, + /// so that the most optimized kernel supported on a host's processor can be chosen. + SimdLevel::type simd_level = SimdLevel::NONE; +}; + +/// \brief Common kernel base data structure for ScalarKernel and +/// VectorKernel. It is called "ArrayKernel" in that the functions generally +/// output array values (as opposed to scalar values in the case of aggregate +/// functions). +struct ArrayKernel : public Kernel { ArrayKernel() = default; - - ArrayKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec, - KernelInit init = NULLPTR) - : Kernel(std::move(sig), init), exec(std::move(exec)) {} - - ArrayKernel(std::vector<InputType> in_types, OutputType out_type, ArrayKernelExec exec, - KernelInit init = NULLPTR) + + ArrayKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec, + KernelInit init = NULLPTR) + : Kernel(std::move(sig), init), exec(std::move(exec)) {} + + ArrayKernel(std::vector<InputType> in_types, OutputType out_type, ArrayKernelExec exec, + KernelInit init = NULLPTR) : Kernel(std::move(in_types), std::move(out_type), std::move(init)), exec(std::move(exec)) {} - - /// \brief Perform a single invocation of this kernel. Depending on the - /// implementation, it may only write into preallocated memory, while in some - /// cases it will allocate its own memory. Any required state is managed - /// through the KernelContext. - ArrayKernelExec exec; - - /// \brief Writing execution results into larger contiguous allocations - /// requires that the kernel be able to write into sliced output ArrayData*, - /// including sliced output validity bitmaps. Some kernel implementations may - /// not be able to do this, so setting this to false disables this - /// functionality. - bool can_write_into_slices = true; -}; - -/// \brief Kernel data structure for implementations of ScalarFunction. In -/// addition to the members found in ArrayKernel, contains the null handling -/// and memory pre-allocation preferences. -struct ScalarKernel : public ArrayKernel { - using ArrayKernel::ArrayKernel; - - // For scalar functions preallocated data and intersecting arg validity - // bitmaps is a reasonable default - NullHandling::type null_handling = NullHandling::INTERSECTION; - MemAllocation::type mem_allocation = MemAllocation::PREALLOCATE; -}; - -// ---------------------------------------------------------------------- -// VectorKernel (for VectorFunction) - -/// \brief See VectorKernel::finalize member for usage + + /// \brief Perform a single invocation of this kernel. Depending on the + /// implementation, it may only write into preallocated memory, while in some + /// cases it will allocate its own memory. Any required state is managed + /// through the KernelContext. + ArrayKernelExec exec; + + /// \brief Writing execution results into larger contiguous allocations + /// requires that the kernel be able to write into sliced output ArrayData*, + /// including sliced output validity bitmaps. Some kernel implementations may + /// not be able to do this, so setting this to false disables this + /// functionality. + bool can_write_into_slices = true; +}; + +/// \brief Kernel data structure for implementations of ScalarFunction. In +/// addition to the members found in ArrayKernel, contains the null handling +/// and memory pre-allocation preferences. +struct ScalarKernel : public ArrayKernel { + using ArrayKernel::ArrayKernel; + + // For scalar functions preallocated data and intersecting arg validity + // bitmaps is a reasonable default + NullHandling::type null_handling = NullHandling::INTERSECTION; + MemAllocation::type mem_allocation = MemAllocation::PREALLOCATE; +}; + +// ---------------------------------------------------------------------- +// VectorKernel (for VectorFunction) + +/// \brief See VectorKernel::finalize member for usage using VectorFinalize = std::function<Status(KernelContext*, std::vector<Datum>*)>; - -/// \brief Kernel data structure for implementations of VectorFunction. In -/// addition to the members found in ArrayKernel, contains an optional -/// finalizer function, the null handling and memory pre-allocation preferences -/// (which have different defaults from ScalarKernel), and some other -/// execution-related options. -struct VectorKernel : public ArrayKernel { + +/// \brief Kernel data structure for implementations of VectorFunction. In +/// addition to the members found in ArrayKernel, contains an optional +/// finalizer function, the null handling and memory pre-allocation preferences +/// (which have different defaults from ScalarKernel), and some other +/// execution-related options. +struct VectorKernel : public ArrayKernel { VectorKernel() = default; - - VectorKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec) + + VectorKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec) : ArrayKernel(std::move(sig), std::move(exec)) {} - - VectorKernel(std::vector<InputType> in_types, OutputType out_type, ArrayKernelExec exec, - KernelInit init = NULLPTR, VectorFinalize finalize = NULLPTR) - : ArrayKernel(std::move(in_types), std::move(out_type), std::move(exec), - std::move(init)), - finalize(std::move(finalize)) {} - - VectorKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec, - KernelInit init = NULLPTR, VectorFinalize finalize = NULLPTR) - : ArrayKernel(std::move(sig), std::move(exec), std::move(init)), - finalize(std::move(finalize)) {} - - /// \brief For VectorKernel, convert intermediate results into finalized - /// results. Mutates input argument. Some kernels may accumulate state - /// (example: hashing-related functions) through processing chunked inputs, and - /// then need to attach some accumulated state to each of the outputs of - /// processing each chunk of data. - VectorFinalize finalize; - - /// Since vector kernels generally are implemented rather differently from - /// scalar/elementwise kernels (and they may not even yield arrays of the same - /// size), so we make the developer opt-in to any memory preallocation rather - /// than having to turn it off. - NullHandling::type null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; - MemAllocation::type mem_allocation = MemAllocation::NO_PREALLOCATE; - - /// Some vector kernels can do chunkwise execution using ExecBatchIterator, - /// in some cases accumulating some state. Other kernels (like Take) need to - /// be passed whole arrays and don't work on ChunkedArray inputs - bool can_execute_chunkwise = true; - - /// Some kernels (like unique and value_counts) yield non-chunked output from - /// chunked-array inputs. This option controls how the results are boxed when - /// returned from ExecVectorFunction - /// - /// true -> ChunkedArray - /// false -> Array - bool output_chunked = true; -}; - -// ---------------------------------------------------------------------- -// ScalarAggregateKernel (for ScalarAggregateFunction) - + + VectorKernel(std::vector<InputType> in_types, OutputType out_type, ArrayKernelExec exec, + KernelInit init = NULLPTR, VectorFinalize finalize = NULLPTR) + : ArrayKernel(std::move(in_types), std::move(out_type), std::move(exec), + std::move(init)), + finalize(std::move(finalize)) {} + + VectorKernel(std::shared_ptr<KernelSignature> sig, ArrayKernelExec exec, + KernelInit init = NULLPTR, VectorFinalize finalize = NULLPTR) + : ArrayKernel(std::move(sig), std::move(exec), std::move(init)), + finalize(std::move(finalize)) {} + + /// \brief For VectorKernel, convert intermediate results into finalized + /// results. Mutates input argument. Some kernels may accumulate state + /// (example: hashing-related functions) through processing chunked inputs, and + /// then need to attach some accumulated state to each of the outputs of + /// processing each chunk of data. + VectorFinalize finalize; + + /// Since vector kernels generally are implemented rather differently from + /// scalar/elementwise kernels (and they may not even yield arrays of the same + /// size), so we make the developer opt-in to any memory preallocation rather + /// than having to turn it off. + NullHandling::type null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; + MemAllocation::type mem_allocation = MemAllocation::NO_PREALLOCATE; + + /// Some vector kernels can do chunkwise execution using ExecBatchIterator, + /// in some cases accumulating some state. Other kernels (like Take) need to + /// be passed whole arrays and don't work on ChunkedArray inputs + bool can_execute_chunkwise = true; + + /// Some kernels (like unique and value_counts) yield non-chunked output from + /// chunked-array inputs. This option controls how the results are boxed when + /// returned from ExecVectorFunction + /// + /// true -> ChunkedArray + /// false -> Array + bool output_chunked = true; +}; + +// ---------------------------------------------------------------------- +// ScalarAggregateKernel (for ScalarAggregateFunction) + using ScalarAggregateConsume = std::function<Status(KernelContext*, const ExecBatch&)>; - -using ScalarAggregateMerge = + +using ScalarAggregateMerge = std::function<Status(KernelContext*, KernelState&&, KernelState*)>; - -// Finalize returns Datum to permit multiple return values + +// Finalize returns Datum to permit multiple return values using ScalarAggregateFinalize = std::function<Status(KernelContext*, Datum*)>; - -/// \brief Kernel data structure for implementations of -/// ScalarAggregateFunction. The four necessary components of an aggregation -/// kernel are the init, consume, merge, and finalize functions. -/// -/// * init: creates a new KernelState for a kernel. -/// * consume: processes an ExecBatch and updates the KernelState found in the -/// KernelContext. -/// * merge: combines one KernelState with another. -/// * finalize: produces the end result of the aggregation using the -/// KernelState in the KernelContext. -struct ScalarAggregateKernel : public Kernel { + +/// \brief Kernel data structure for implementations of +/// ScalarAggregateFunction. The four necessary components of an aggregation +/// kernel are the init, consume, merge, and finalize functions. +/// +/// * init: creates a new KernelState for a kernel. +/// * consume: processes an ExecBatch and updates the KernelState found in the +/// KernelContext. +/// * merge: combines one KernelState with another. +/// * finalize: produces the end result of the aggregation using the +/// KernelState in the KernelContext. +struct ScalarAggregateKernel : public Kernel { ScalarAggregateKernel() = default; - - ScalarAggregateKernel(std::shared_ptr<KernelSignature> sig, KernelInit init, - ScalarAggregateConsume consume, ScalarAggregateMerge merge, - ScalarAggregateFinalize finalize) + + ScalarAggregateKernel(std::shared_ptr<KernelSignature> sig, KernelInit init, + ScalarAggregateConsume consume, ScalarAggregateMerge merge, + ScalarAggregateFinalize finalize) : Kernel(std::move(sig), std::move(init)), - consume(std::move(consume)), - merge(std::move(merge)), - finalize(std::move(finalize)) {} - - ScalarAggregateKernel(std::vector<InputType> in_types, OutputType out_type, - KernelInit init, ScalarAggregateConsume consume, - ScalarAggregateMerge merge, ScalarAggregateFinalize finalize) + consume(std::move(consume)), + merge(std::move(merge)), + finalize(std::move(finalize)) {} + + ScalarAggregateKernel(std::vector<InputType> in_types, OutputType out_type, + KernelInit init, ScalarAggregateConsume consume, + ScalarAggregateMerge merge, ScalarAggregateFinalize finalize) : ScalarAggregateKernel( KernelSignature::Make(std::move(in_types), std::move(out_type)), std::move(init), std::move(consume), std::move(merge), std::move(finalize)) {} - + /// \brief Merge a vector of KernelStates into a single KernelState. /// The merged state will be returned and will be set on the KernelContext. static Result<std::unique_ptr<KernelState>> MergeAll( const ScalarAggregateKernel* kernel, KernelContext* ctx, std::vector<std::unique_ptr<KernelState>> states); - ScalarAggregateConsume consume; - ScalarAggregateMerge merge; - ScalarAggregateFinalize finalize; -}; - + ScalarAggregateConsume consume; + ScalarAggregateMerge merge; + ScalarAggregateFinalize finalize; +}; + // ---------------------------------------------------------------------- // HashAggregateKernel (for HashAggregateFunction) @@ -735,5 +735,5 @@ struct HashAggregateKernel : public Kernel { HashAggregateFinalize finalize; }; -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic.cc index 88f3b87d9e..a7df66695b 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic.cc @@ -1,44 +1,44 @@ -// 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. - -#include "arrow/compute/api_aggregate.h" -#include "arrow/compute/kernels/aggregate_basic_internal.h" -#include "arrow/compute/kernels/aggregate_internal.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/util/cpu_info.h" -#include "arrow/util/make_unique.h" - -namespace arrow { -namespace compute { - +// 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. + +#include "arrow/compute/api_aggregate.h" +#include "arrow/compute/kernels/aggregate_basic_internal.h" +#include "arrow/compute/kernels/aggregate_internal.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/util/cpu_info.h" +#include "arrow/util/make_unique.h" + +namespace arrow { +namespace compute { + namespace { Status AggregateConsume(KernelContext* ctx, const ExecBatch& batch) { return checked_cast<ScalarAggregator*>(ctx->state())->Consume(ctx, batch); -} - +} + Status AggregateMerge(KernelContext* ctx, KernelState&& src, KernelState* dst) { return checked_cast<ScalarAggregator*>(dst)->MergeFrom(ctx, std::move(src)); -} - +} + Status AggregateFinalize(KernelContext* ctx, Datum* out) { return checked_cast<ScalarAggregator*>(ctx->state())->Finalize(ctx, out); -} - +} + } // namespace void AddAggKernel(std::shared_ptr<KernelSignature> sig, KernelInit init, @@ -52,12 +52,12 @@ void AddAggKernel(std::shared_ptr<KernelSignature> sig, KernelInit init, namespace aggregate { -// ---------------------------------------------------------------------- -// Count implementation - -struct CountImpl : public ScalarAggregator { +// ---------------------------------------------------------------------- +// Count implementation + +struct CountImpl : public ScalarAggregator { explicit CountImpl(ScalarAggregateOptions options) : options(std::move(options)) {} - + Status Consume(KernelContext*, const ExecBatch& batch) override { if (batch[0].is_array()) { const ArrayData& input = *batch[0].array(); @@ -70,80 +70,80 @@ struct CountImpl : public ScalarAggregator { this->non_nulls += input.is_valid * batch.length; } return Status::OK(); - } - + } + Status MergeFrom(KernelContext*, KernelState&& src) override { - const auto& other_state = checked_cast<const CountImpl&>(src); - this->non_nulls += other_state.non_nulls; - this->nulls += other_state.nulls; + const auto& other_state = checked_cast<const CountImpl&>(src); + this->non_nulls += other_state.non_nulls; + this->nulls += other_state.nulls; return Status::OK(); - } - + } + Status Finalize(KernelContext* ctx, Datum* out) override { - const auto& state = checked_cast<const CountImpl&>(*ctx->state()); + const auto& state = checked_cast<const CountImpl&>(*ctx->state()); if (state.options.skip_nulls) { *out = Datum(state.non_nulls); } else { *out = Datum(state.nulls); - } + } return Status::OK(); - } - + } + ScalarAggregateOptions options; - int64_t non_nulls = 0; - int64_t nulls = 0; -}; - + int64_t non_nulls = 0; + int64_t nulls = 0; +}; + Result<std::unique_ptr<KernelState>> CountInit(KernelContext*, const KernelInitArgs& args) { - return ::arrow::internal::make_unique<CountImpl>( + return ::arrow::internal::make_unique<CountImpl>( static_cast<const ScalarAggregateOptions&>(*args.options)); -} - -// ---------------------------------------------------------------------- -// Sum implementation - +} + +// ---------------------------------------------------------------------- +// Sum implementation + template <typename ArrowType> struct SumImplDefault : public SumImpl<ArrowType, SimdLevel::NONE> { explicit SumImplDefault(const ScalarAggregateOptions& options_) { this->options = options_; } -}; - +}; + template <typename ArrowType> struct MeanImplDefault : public MeanImpl<ArrowType, SimdLevel::NONE> { explicit MeanImplDefault(const ScalarAggregateOptions& options_) { this->options = options_; } -}; - +}; + Result<std::unique_ptr<KernelState>> SumInit(KernelContext* ctx, const KernelInitArgs& args) { SumLikeInit<SumImplDefault> visitor( ctx, *args.inputs[0].type, static_cast<const ScalarAggregateOptions&>(*args.options)); - return visitor.Create(); -} - + return visitor.Create(); +} + Result<std::unique_ptr<KernelState>> MeanInit(KernelContext* ctx, const KernelInitArgs& args) { SumLikeInit<MeanImplDefault> visitor( ctx, *args.inputs[0].type, static_cast<const ScalarAggregateOptions&>(*args.options)); - return visitor.Create(); -} - -// ---------------------------------------------------------------------- -// MinMax implementation - + return visitor.Create(); +} + +// ---------------------------------------------------------------------- +// MinMax implementation + Result<std::unique_ptr<KernelState>> MinMaxInit(KernelContext* ctx, const KernelInitArgs& args) { - MinMaxInitState<SimdLevel::NONE> visitor( - ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), + MinMaxInitState<SimdLevel::NONE> visitor( + ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), static_cast<const ScalarAggregateOptions&>(*args.options)); - return visitor.Create(); -} - + return visitor.Create(); +} + // ---------------------------------------------------------------------- // Any implementation @@ -203,8 +203,8 @@ Result<std::unique_ptr<KernelState>> AnyInit(KernelContext*, const KernelInitArg static_cast<const ScalarAggregateOptions&>(*args.options); return ::arrow::internal::make_unique<BooleanAnyImpl>( static_cast<const ScalarAggregateOptions&>(*args.options)); -} - +} + // ---------------------------------------------------------------------- // All implementation @@ -394,17 +394,17 @@ struct IndexInit { } }; -void AddBasicAggKernels(KernelInit init, - const std::vector<std::shared_ptr<DataType>>& types, - std::shared_ptr<DataType> out_ty, ScalarAggregateFunction* func, - SimdLevel::type simd_level) { - for (const auto& ty : types) { - // array[InT] -> scalar[OutT] - auto sig = KernelSignature::Make({InputType::Array(ty)}, ValueDescr::Scalar(out_ty)); - AddAggKernel(std::move(sig), init, func, simd_level); - } -} - +void AddBasicAggKernels(KernelInit init, + const std::vector<std::shared_ptr<DataType>>& types, + std::shared_ptr<DataType> out_ty, ScalarAggregateFunction* func, + SimdLevel::type simd_level) { + for (const auto& ty : types) { + // array[InT] -> scalar[OutT] + auto sig = KernelSignature::Make({InputType::Array(ty)}, ValueDescr::Scalar(out_ty)); + AddAggKernel(std::move(sig), init, func, simd_level); + } +} + void AddScalarAggKernels(KernelInit init, const std::vector<std::shared_ptr<DataType>>& types, std::shared_ptr<DataType> out_ty, @@ -425,20 +425,20 @@ void AddArrayScalarAggKernels(KernelInit init, AddScalarAggKernels(init, types, out_ty, func); } -void AddMinMaxKernels(KernelInit init, - const std::vector<std::shared_ptr<DataType>>& types, - ScalarAggregateFunction* func, SimdLevel::type simd_level) { - for (const auto& ty : types) { +void AddMinMaxKernels(KernelInit init, + const std::vector<std::shared_ptr<DataType>>& types, + ScalarAggregateFunction* func, SimdLevel::type simd_level) { + for (const auto& ty : types) { // any[T] -> scalar[struct<min: T, max: T>] - auto out_ty = struct_({field("min", ty), field("max", ty)}); + auto out_ty = struct_({field("min", ty), field("max", ty)}); auto sig = KernelSignature::Make({InputType(ty)}, ValueDescr::Scalar(out_ty)); - AddAggKernel(std::move(sig), init, func, simd_level); - } -} - -} // namespace aggregate - -namespace internal { + AddAggKernel(std::move(sig), init, func, simd_level); + } +} + +} // namespace aggregate + +namespace internal { namespace { const FunctionDoc count_doc{"Count the number of null / non-null values", @@ -496,21 +496,21 @@ const FunctionDoc index_doc{"Find the index of the first occurrence of a given v } // namespace -void RegisterScalarAggregateBasic(FunctionRegistry* registry) { +void RegisterScalarAggregateBasic(FunctionRegistry* registry) { static auto default_scalar_aggregate_options = ScalarAggregateOptions::Defaults(); - + auto func = std::make_shared<ScalarAggregateFunction>( "count", Arity::Unary(), &count_doc, &default_scalar_aggregate_options); - // Takes any array input, outputs int64 scalar - InputType any_array(ValueDescr::ARRAY); + // Takes any array input, outputs int64 scalar + InputType any_array(ValueDescr::ARRAY); AddAggKernel(KernelSignature::Make({any_array}, ValueDescr::Scalar(int64())), aggregate::CountInit, func.get()); AddAggKernel( KernelSignature::Make({InputType(ValueDescr::SCALAR)}, ValueDescr::Scalar(int64())), aggregate::CountInit, func.get()); - DCHECK_OK(registry->AddFunction(std::move(func))); - + DCHECK_OK(registry->AddFunction(std::move(func))); + func = std::make_shared<ScalarAggregateFunction>("sum", Arity::Unary(), &sum_doc, &default_scalar_aggregate_options); aggregate::AddArrayScalarAggKernels(aggregate::SumInit, {boolean()}, int64(), @@ -521,59 +521,59 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { func.get()); aggregate::AddArrayScalarAggKernels(aggregate::SumInit, FloatingPointTypes(), float64(), func.get()); - // Add the SIMD variants for sum + // Add the SIMD variants for sum #if defined(ARROW_HAVE_RUNTIME_AVX2) || defined(ARROW_HAVE_RUNTIME_AVX512) - auto cpu_info = arrow::internal::CpuInfo::GetInstance(); + auto cpu_info = arrow::internal::CpuInfo::GetInstance(); +#endif +#if defined(ARROW_HAVE_RUNTIME_AVX2) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { + aggregate::AddSumAvx2AggKernels(func.get()); + } +#endif +#if defined(ARROW_HAVE_RUNTIME_AVX512) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { + aggregate::AddSumAvx512AggKernels(func.get()); + } #endif -#if defined(ARROW_HAVE_RUNTIME_AVX2) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { - aggregate::AddSumAvx2AggKernels(func.get()); - } -#endif -#if defined(ARROW_HAVE_RUNTIME_AVX512) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { - aggregate::AddSumAvx512AggKernels(func.get()); - } -#endif - DCHECK_OK(registry->AddFunction(std::move(func))); - + DCHECK_OK(registry->AddFunction(std::move(func))); + func = std::make_shared<ScalarAggregateFunction>("mean", Arity::Unary(), &mean_doc, &default_scalar_aggregate_options); aggregate::AddArrayScalarAggKernels(aggregate::MeanInit, {boolean()}, float64(), func.get()); aggregate::AddArrayScalarAggKernels(aggregate::MeanInit, NumericTypes(), float64(), func.get()); - // Add the SIMD variants for mean -#if defined(ARROW_HAVE_RUNTIME_AVX2) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { - aggregate::AddMeanAvx2AggKernels(func.get()); - } -#endif -#if defined(ARROW_HAVE_RUNTIME_AVX512) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { - aggregate::AddMeanAvx512AggKernels(func.get()); - } -#endif - DCHECK_OK(registry->AddFunction(std::move(func))); - + // Add the SIMD variants for mean +#if defined(ARROW_HAVE_RUNTIME_AVX2) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { + aggregate::AddMeanAvx2AggKernels(func.get()); + } +#endif +#if defined(ARROW_HAVE_RUNTIME_AVX512) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { + aggregate::AddMeanAvx512AggKernels(func.get()); + } +#endif + DCHECK_OK(registry->AddFunction(std::move(func))); + func = std::make_shared<ScalarAggregateFunction>( "min_max", Arity::Unary(), &min_max_doc, &default_scalar_aggregate_options); - aggregate::AddMinMaxKernels(aggregate::MinMaxInit, {boolean()}, func.get()); - aggregate::AddMinMaxKernels(aggregate::MinMaxInit, NumericTypes(), func.get()); - // Add the SIMD variants for min max -#if defined(ARROW_HAVE_RUNTIME_AVX2) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { - aggregate::AddMinMaxAvx2AggKernels(func.get()); - } -#endif -#if defined(ARROW_HAVE_RUNTIME_AVX512) - if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { - aggregate::AddMinMaxAvx512AggKernels(func.get()); - } -#endif - - DCHECK_OK(registry->AddFunction(std::move(func))); - + aggregate::AddMinMaxKernels(aggregate::MinMaxInit, {boolean()}, func.get()); + aggregate::AddMinMaxKernels(aggregate::MinMaxInit, NumericTypes(), func.get()); + // Add the SIMD variants for min max +#if defined(ARROW_HAVE_RUNTIME_AVX2) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX2)) { + aggregate::AddMinMaxAvx2AggKernels(func.get()); + } +#endif +#if defined(ARROW_HAVE_RUNTIME_AVX512) + if (cpu_info->IsSupported(arrow::internal::CpuInfo::AVX512)) { + aggregate::AddMinMaxAvx512AggKernels(func.get()); + } +#endif + + DCHECK_OK(registry->AddFunction(std::move(func))); + // any func = std::make_shared<ScalarAggregateFunction>("any", Arity::Unary(), &any_doc, &default_scalar_aggregate_options); @@ -597,8 +597,8 @@ void RegisterScalarAggregateBasic(FunctionRegistry* registry) { aggregate::AddBasicAggKernels(aggregate::IndexInit::Init, TemporalTypes(), int64(), func.get()); DCHECK_OK(registry->AddFunction(std::move(func))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic_internal.h index 60419356c5..5163d3fd03 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_basic_internal.h @@ -1,63 +1,63 @@ -// 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 <cmath> - -#include "arrow/compute/api_aggregate.h" -#include "arrow/compute/kernels/aggregate_internal.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/util/align_util.h" -#include "arrow/util/bit_block_counter.h" - -namespace arrow { -namespace compute { -namespace aggregate { - -void AddBasicAggKernels(KernelInit init, - const std::vector<std::shared_ptr<DataType>>& types, - std::shared_ptr<DataType> out_ty, ScalarAggregateFunction* func, - SimdLevel::type simd_level = SimdLevel::NONE); - -void AddMinMaxKernels(KernelInit init, - const std::vector<std::shared_ptr<DataType>>& types, - ScalarAggregateFunction* func, - SimdLevel::type simd_level = SimdLevel::NONE); - -// SIMD variants for kernels -void AddSumAvx2AggKernels(ScalarAggregateFunction* func); -void AddMeanAvx2AggKernels(ScalarAggregateFunction* func); -void AddMinMaxAvx2AggKernels(ScalarAggregateFunction* func); - -void AddSumAvx512AggKernels(ScalarAggregateFunction* func); -void AddMeanAvx512AggKernels(ScalarAggregateFunction* func); -void AddMinMaxAvx512AggKernels(ScalarAggregateFunction* func); - -// ---------------------------------------------------------------------- -// Sum implementation - +// 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 <cmath> + +#include "arrow/compute/api_aggregate.h" +#include "arrow/compute/kernels/aggregate_internal.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/util/align_util.h" +#include "arrow/util/bit_block_counter.h" + +namespace arrow { +namespace compute { +namespace aggregate { + +void AddBasicAggKernels(KernelInit init, + const std::vector<std::shared_ptr<DataType>>& types, + std::shared_ptr<DataType> out_ty, ScalarAggregateFunction* func, + SimdLevel::type simd_level = SimdLevel::NONE); + +void AddMinMaxKernels(KernelInit init, + const std::vector<std::shared_ptr<DataType>>& types, + ScalarAggregateFunction* func, + SimdLevel::type simd_level = SimdLevel::NONE); + +// SIMD variants for kernels +void AddSumAvx2AggKernels(ScalarAggregateFunction* func); +void AddMeanAvx2AggKernels(ScalarAggregateFunction* func); +void AddMinMaxAvx2AggKernels(ScalarAggregateFunction* func); + +void AddSumAvx512AggKernels(ScalarAggregateFunction* func); +void AddMeanAvx512AggKernels(ScalarAggregateFunction* func); +void AddMinMaxAvx512AggKernels(ScalarAggregateFunction* func); + +// ---------------------------------------------------------------------- +// Sum implementation + template <typename ArrowType, SimdLevel::type SimdLevel> struct SumImpl : public ScalarAggregator { using ThisType = SumImpl<ArrowType, SimdLevel>; using CType = typename ArrowType::c_type; - using SumType = typename FindAccumulatorType<ArrowType>::Type; + using SumType = typename FindAccumulatorType<ArrowType>::Type; using OutputType = typename TypeTraits<SumType>::ScalarType; - + Status Consume(KernelContext*, const ExecBatch& batch) override { if (batch[0].is_array()) { const auto& data = batch[0].array(); @@ -70,173 +70,173 @@ struct SumImpl : public ScalarAggregator { arrow::compute::detail::SumArray<CType, typename SumType::c_type, SimdLevel>( *data); } - } else { + } else { const auto& data = *batch[0].scalar(); this->count += data.is_valid * batch.length; if (data.is_valid) { this->sum += internal::UnboxScalar<ArrowType>::Unbox(data) * batch.length; - } - } + } + } return Status::OK(); - } - + } + Status MergeFrom(KernelContext*, KernelState&& src) override { const auto& other = checked_cast<const ThisType&>(src); this->count += other.count; this->sum += other.sum; return Status::OK(); - } - + } + Status Finalize(KernelContext*, Datum* out) override { if (this->count < options.min_count) { out->value = std::make_shared<OutputType>(); - } else { + } else { out->value = MakeScalar(this->sum); - } + } return Status::OK(); - } - - size_t count = 0; - typename SumType::c_type sum = 0; + } + + size_t count = 0; + typename SumType::c_type sum = 0; ScalarAggregateOptions options; -}; - +}; + template <typename ArrowType, SimdLevel::type SimdLevel> struct MeanImpl : public SumImpl<ArrowType, SimdLevel> { Status Finalize(KernelContext*, Datum* out) override { if (this->count < options.min_count) { out->value = std::make_shared<DoubleScalar>(); - } else { + } else { const double mean = static_cast<double>(this->sum) / this->count; out->value = std::make_shared<DoubleScalar>(mean); - } + } return Status::OK(); - } + } ScalarAggregateOptions options; -}; - -template <template <typename> class KernelClass> -struct SumLikeInit { - std::unique_ptr<KernelState> state; - KernelContext* ctx; - const DataType& type; +}; + +template <template <typename> class KernelClass> +struct SumLikeInit { + std::unique_ptr<KernelState> state; + KernelContext* ctx; + const DataType& type; const ScalarAggregateOptions& options; - + SumLikeInit(KernelContext* ctx, const DataType& type, const ScalarAggregateOptions& options) : ctx(ctx), type(type), options(options) {} - - Status Visit(const DataType&) { return Status::NotImplemented("No sum implemented"); } - - Status Visit(const HalfFloatType&) { - return Status::NotImplemented("No sum implemented"); - } - - Status Visit(const BooleanType&) { + + Status Visit(const DataType&) { return Status::NotImplemented("No sum implemented"); } + + Status Visit(const HalfFloatType&) { + return Status::NotImplemented("No sum implemented"); + } + + Status Visit(const BooleanType&) { state.reset(new KernelClass<BooleanType>(options)); - return Status::OK(); - } - - template <typename Type> - enable_if_number<Type, Status> Visit(const Type&) { + return Status::OK(); + } + + template <typename Type> + enable_if_number<Type, Status> Visit(const Type&) { state.reset(new KernelClass<Type>(options)); - return Status::OK(); - } - + return Status::OK(); + } + Result<std::unique_ptr<KernelState>> Create() { RETURN_NOT_OK(VisitTypeInline(type, this)); - return std::move(state); - } -}; - -// ---------------------------------------------------------------------- -// MinMax implementation - -template <typename ArrowType, SimdLevel::type SimdLevel, typename Enable = void> -struct MinMaxState {}; - -template <typename ArrowType, SimdLevel::type SimdLevel> -struct MinMaxState<ArrowType, SimdLevel, enable_if_boolean<ArrowType>> { - using ThisType = MinMaxState<ArrowType, SimdLevel>; - using T = typename ArrowType::c_type; - - ThisType& operator+=(const ThisType& rhs) { - this->has_nulls |= rhs.has_nulls; - this->has_values |= rhs.has_values; - this->min = this->min && rhs.min; - this->max = this->max || rhs.max; - return *this; - } - - void MergeOne(T value) { - this->min = this->min && value; - this->max = this->max || value; - } - - T min = true; - T max = false; - bool has_nulls = false; - bool has_values = false; -}; - -template <typename ArrowType, SimdLevel::type SimdLevel> -struct MinMaxState<ArrowType, SimdLevel, enable_if_integer<ArrowType>> { - using ThisType = MinMaxState<ArrowType, SimdLevel>; - using T = typename ArrowType::c_type; - - ThisType& operator+=(const ThisType& rhs) { - this->has_nulls |= rhs.has_nulls; - this->has_values |= rhs.has_values; - this->min = std::min(this->min, rhs.min); - this->max = std::max(this->max, rhs.max); - return *this; - } - - void MergeOne(T value) { - this->min = std::min(this->min, value); - this->max = std::max(this->max, value); - } - - T min = std::numeric_limits<T>::max(); - T max = std::numeric_limits<T>::min(); - bool has_nulls = false; - bool has_values = false; -}; - -template <typename ArrowType, SimdLevel::type SimdLevel> -struct MinMaxState<ArrowType, SimdLevel, enable_if_floating_point<ArrowType>> { - using ThisType = MinMaxState<ArrowType, SimdLevel>; - using T = typename ArrowType::c_type; - - ThisType& operator+=(const ThisType& rhs) { - this->has_nulls |= rhs.has_nulls; - this->has_values |= rhs.has_values; - this->min = std::fmin(this->min, rhs.min); - this->max = std::fmax(this->max, rhs.max); - return *this; - } - - void MergeOne(T value) { - this->min = std::fmin(this->min, value); - this->max = std::fmax(this->max, value); - } - - T min = std::numeric_limits<T>::infinity(); - T max = -std::numeric_limits<T>::infinity(); - bool has_nulls = false; - bool has_values = false; -}; - -template <typename ArrowType, SimdLevel::type SimdLevel> -struct MinMaxImpl : public ScalarAggregator { - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; - using ThisType = MinMaxImpl<ArrowType, SimdLevel>; - using StateType = MinMaxState<ArrowType, SimdLevel>; - + return std::move(state); + } +}; + +// ---------------------------------------------------------------------- +// MinMax implementation + +template <typename ArrowType, SimdLevel::type SimdLevel, typename Enable = void> +struct MinMaxState {}; + +template <typename ArrowType, SimdLevel::type SimdLevel> +struct MinMaxState<ArrowType, SimdLevel, enable_if_boolean<ArrowType>> { + using ThisType = MinMaxState<ArrowType, SimdLevel>; + using T = typename ArrowType::c_type; + + ThisType& operator+=(const ThisType& rhs) { + this->has_nulls |= rhs.has_nulls; + this->has_values |= rhs.has_values; + this->min = this->min && rhs.min; + this->max = this->max || rhs.max; + return *this; + } + + void MergeOne(T value) { + this->min = this->min && value; + this->max = this->max || value; + } + + T min = true; + T max = false; + bool has_nulls = false; + bool has_values = false; +}; + +template <typename ArrowType, SimdLevel::type SimdLevel> +struct MinMaxState<ArrowType, SimdLevel, enable_if_integer<ArrowType>> { + using ThisType = MinMaxState<ArrowType, SimdLevel>; + using T = typename ArrowType::c_type; + + ThisType& operator+=(const ThisType& rhs) { + this->has_nulls |= rhs.has_nulls; + this->has_values |= rhs.has_values; + this->min = std::min(this->min, rhs.min); + this->max = std::max(this->max, rhs.max); + return *this; + } + + void MergeOne(T value) { + this->min = std::min(this->min, value); + this->max = std::max(this->max, value); + } + + T min = std::numeric_limits<T>::max(); + T max = std::numeric_limits<T>::min(); + bool has_nulls = false; + bool has_values = false; +}; + +template <typename ArrowType, SimdLevel::type SimdLevel> +struct MinMaxState<ArrowType, SimdLevel, enable_if_floating_point<ArrowType>> { + using ThisType = MinMaxState<ArrowType, SimdLevel>; + using T = typename ArrowType::c_type; + + ThisType& operator+=(const ThisType& rhs) { + this->has_nulls |= rhs.has_nulls; + this->has_values |= rhs.has_values; + this->min = std::fmin(this->min, rhs.min); + this->max = std::fmax(this->max, rhs.max); + return *this; + } + + void MergeOne(T value) { + this->min = std::fmin(this->min, value); + this->max = std::fmax(this->max, value); + } + + T min = std::numeric_limits<T>::infinity(); + T max = -std::numeric_limits<T>::infinity(); + bool has_nulls = false; + bool has_values = false; +}; + +template <typename ArrowType, SimdLevel::type SimdLevel> +struct MinMaxImpl : public ScalarAggregator { + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; + using ThisType = MinMaxImpl<ArrowType, SimdLevel>; + using StateType = MinMaxState<ArrowType, SimdLevel>; + MinMaxImpl(const std::shared_ptr<DataType>& out_type, const ScalarAggregateOptions& options) - : out_type(out_type), options(options) {} - + : out_type(out_type), options(options) {} + Status Consume(KernelContext*, const ExecBatch& batch) override { if (batch[0].is_array()) { return ConsumeArray(ArrayType(batch[0].array())); @@ -245,15 +245,15 @@ struct MinMaxImpl : public ScalarAggregator { } Status ConsumeScalar(const Scalar& scalar) { - StateType local; + StateType local; local.has_nulls = !scalar.is_valid; local.has_values = scalar.is_valid; - + if (local.has_nulls && !options.skip_nulls) { this->state = local; return Status::OK(); } - + local.MergeOne(internal::UnboxScalar<ArrowType>::Unbox(scalar)); this->state = local; return Status::OK(); @@ -262,143 +262,143 @@ struct MinMaxImpl : public ScalarAggregator { Status ConsumeArray(const ArrayType& arr) { StateType local; - const auto null_count = arr.null_count(); - local.has_nulls = null_count > 0; - local.has_values = (arr.length() - null_count) > 0; - + const auto null_count = arr.null_count(); + local.has_nulls = null_count > 0; + local.has_values = (arr.length() - null_count) > 0; + if (local.has_nulls && !options.skip_nulls) { - this->state = local; + this->state = local; return Status::OK(); - } - - if (local.has_nulls) { - local += ConsumeWithNulls(arr); - } else { // All true values - for (int64_t i = 0; i < arr.length(); i++) { - local.MergeOne(arr.Value(i)); - } - } - this->state = local; + } + + if (local.has_nulls) { + local += ConsumeWithNulls(arr); + } else { // All true values + for (int64_t i = 0; i < arr.length(); i++) { + local.MergeOne(arr.Value(i)); + } + } + this->state = local; return Status::OK(); - } - + } + Status MergeFrom(KernelContext*, KernelState&& src) override { - const auto& other = checked_cast<const ThisType&>(src); - this->state += other.state; + const auto& other = checked_cast<const ThisType&>(src); + this->state += other.state; return Status::OK(); - } - + } + Status Finalize(KernelContext*, Datum* out) override { - using ScalarType = typename TypeTraits<ArrowType>::ScalarType; - - std::vector<std::shared_ptr<Scalar>> values; + using ScalarType = typename TypeTraits<ArrowType>::ScalarType; + + std::vector<std::shared_ptr<Scalar>> values; if (!state.has_values || (state.has_nulls && !options.skip_nulls)) { - // (null, null) - values = {std::make_shared<ScalarType>(), std::make_shared<ScalarType>()}; - } else { - values = {std::make_shared<ScalarType>(state.min), - std::make_shared<ScalarType>(state.max)}; - } + // (null, null) + values = {std::make_shared<ScalarType>(), std::make_shared<ScalarType>()}; + } else { + values = {std::make_shared<ScalarType>(state.min), + std::make_shared<ScalarType>(state.max)}; + } out->value = std::make_shared<StructScalar>(std::move(values), this->out_type); return Status::OK(); - } - - std::shared_ptr<DataType> out_type; + } + + std::shared_ptr<DataType> out_type; ScalarAggregateOptions options; - MinMaxState<ArrowType, SimdLevel> state; - - private: - StateType ConsumeWithNulls(const ArrayType& arr) const { - StateType local; - const int64_t length = arr.length(); - int64_t offset = arr.offset(); - const uint8_t* bitmap = arr.null_bitmap_data(); - int64_t idx = 0; - - const auto p = arrow::internal::BitmapWordAlign<1>(bitmap, offset, length); - // First handle the leading bits - const int64_t leading_bits = p.leading_bits; - while (idx < leading_bits) { - if (BitUtil::GetBit(bitmap, offset)) { - local.MergeOne(arr.Value(idx)); - } - idx++; - offset++; - } - - // The aligned parts scanned with BitBlockCounter - arrow::internal::BitBlockCounter data_counter(bitmap, offset, length - leading_bits); - auto current_block = data_counter.NextWord(); - while (idx < length) { - if (current_block.AllSet()) { // All true values - int run_length = 0; - // Scan forward until a block that has some false values (or the end) - while (current_block.length > 0 && current_block.AllSet()) { - run_length += current_block.length; - current_block = data_counter.NextWord(); - } - for (int64_t i = 0; i < run_length; i++) { - local.MergeOne(arr.Value(idx + i)); - } - idx += run_length; - offset += run_length; - // The current_block already computed, advance to next loop - continue; - } else if (!current_block.NoneSet()) { // Some values are null - BitmapReader reader(arr.null_bitmap_data(), offset, current_block.length); - for (int64_t i = 0; i < current_block.length; i++) { - if (reader.IsSet()) { - local.MergeOne(arr.Value(idx + i)); - } - reader.Next(); - } - - idx += current_block.length; - offset += current_block.length; - } else { // All null values - idx += current_block.length; - offset += current_block.length; - } - current_block = data_counter.NextWord(); - } - - return local; - } -}; - -template <SimdLevel::type SimdLevel> -struct BooleanMinMaxImpl : public MinMaxImpl<BooleanType, SimdLevel> { - using StateType = MinMaxState<BooleanType, SimdLevel>; - using ArrayType = typename TypeTraits<BooleanType>::ArrayType; - using MinMaxImpl<BooleanType, SimdLevel>::MinMaxImpl; - using MinMaxImpl<BooleanType, SimdLevel>::options; - + MinMaxState<ArrowType, SimdLevel> state; + + private: + StateType ConsumeWithNulls(const ArrayType& arr) const { + StateType local; + const int64_t length = arr.length(); + int64_t offset = arr.offset(); + const uint8_t* bitmap = arr.null_bitmap_data(); + int64_t idx = 0; + + const auto p = arrow::internal::BitmapWordAlign<1>(bitmap, offset, length); + // First handle the leading bits + const int64_t leading_bits = p.leading_bits; + while (idx < leading_bits) { + if (BitUtil::GetBit(bitmap, offset)) { + local.MergeOne(arr.Value(idx)); + } + idx++; + offset++; + } + + // The aligned parts scanned with BitBlockCounter + arrow::internal::BitBlockCounter data_counter(bitmap, offset, length - leading_bits); + auto current_block = data_counter.NextWord(); + while (idx < length) { + if (current_block.AllSet()) { // All true values + int run_length = 0; + // Scan forward until a block that has some false values (or the end) + while (current_block.length > 0 && current_block.AllSet()) { + run_length += current_block.length; + current_block = data_counter.NextWord(); + } + for (int64_t i = 0; i < run_length; i++) { + local.MergeOne(arr.Value(idx + i)); + } + idx += run_length; + offset += run_length; + // The current_block already computed, advance to next loop + continue; + } else if (!current_block.NoneSet()) { // Some values are null + BitmapReader reader(arr.null_bitmap_data(), offset, current_block.length); + for (int64_t i = 0; i < current_block.length; i++) { + if (reader.IsSet()) { + local.MergeOne(arr.Value(idx + i)); + } + reader.Next(); + } + + idx += current_block.length; + offset += current_block.length; + } else { // All null values + idx += current_block.length; + offset += current_block.length; + } + current_block = data_counter.NextWord(); + } + + return local; + } +}; + +template <SimdLevel::type SimdLevel> +struct BooleanMinMaxImpl : public MinMaxImpl<BooleanType, SimdLevel> { + using StateType = MinMaxState<BooleanType, SimdLevel>; + using ArrayType = typename TypeTraits<BooleanType>::ArrayType; + using MinMaxImpl<BooleanType, SimdLevel>::MinMaxImpl; + using MinMaxImpl<BooleanType, SimdLevel>::options; + Status Consume(KernelContext*, const ExecBatch& batch) override { if (ARROW_PREDICT_FALSE(batch[0].is_scalar())) { return ConsumeScalar(checked_cast<const BooleanScalar&>(*batch[0].scalar())); } - StateType local; - ArrayType arr(batch[0].array()); - - const auto arr_length = arr.length(); - const auto null_count = arr.null_count(); - const auto valid_count = arr_length - null_count; - - local.has_nulls = null_count > 0; - local.has_values = valid_count > 0; + StateType local; + ArrayType arr(batch[0].array()); + + const auto arr_length = arr.length(); + const auto null_count = arr.null_count(); + const auto valid_count = arr_length - null_count; + + local.has_nulls = null_count > 0; + local.has_values = valid_count > 0; if (local.has_nulls && !options.skip_nulls) { - this->state = local; + this->state = local; return Status::OK(); - } - - const auto true_count = arr.true_count(); - const auto false_count = valid_count - true_count; - local.max = true_count > 0; - local.min = false_count == 0; - - this->state = local; + } + + const auto true_count = arr.true_count(); + const auto false_count = valid_count - true_count; + local.max = true_count > 0; + local.min = false_count == 0; + + this->state = local; return Status::OK(); - } + } Status ConsumeScalar(const BooleanScalar& scalar) { StateType local; @@ -418,46 +418,46 @@ struct BooleanMinMaxImpl : public MinMaxImpl<BooleanType, SimdLevel> { this->state = local; return Status::OK(); } -}; - -template <SimdLevel::type SimdLevel> -struct MinMaxInitState { - std::unique_ptr<KernelState> state; - KernelContext* ctx; - const DataType& in_type; - const std::shared_ptr<DataType>& out_type; +}; + +template <SimdLevel::type SimdLevel> +struct MinMaxInitState { + std::unique_ptr<KernelState> state; + KernelContext* ctx; + const DataType& in_type; + const std::shared_ptr<DataType>& out_type; const ScalarAggregateOptions& options; - - MinMaxInitState(KernelContext* ctx, const DataType& in_type, + + MinMaxInitState(KernelContext* ctx, const DataType& in_type, const std::shared_ptr<DataType>& out_type, const ScalarAggregateOptions& options) - : ctx(ctx), in_type(in_type), out_type(out_type), options(options) {} - - Status Visit(const DataType&) { - return Status::NotImplemented("No min/max implemented"); - } - - Status Visit(const HalfFloatType&) { - return Status::NotImplemented("No min/max implemented"); - } - - Status Visit(const BooleanType&) { - state.reset(new BooleanMinMaxImpl<SimdLevel>(out_type, options)); - return Status::OK(); - } - - template <typename Type> - enable_if_number<Type, Status> Visit(const Type&) { - state.reset(new MinMaxImpl<Type, SimdLevel>(out_type, options)); - return Status::OK(); - } - + : ctx(ctx), in_type(in_type), out_type(out_type), options(options) {} + + Status Visit(const DataType&) { + return Status::NotImplemented("No min/max implemented"); + } + + Status Visit(const HalfFloatType&) { + return Status::NotImplemented("No min/max implemented"); + } + + Status Visit(const BooleanType&) { + state.reset(new BooleanMinMaxImpl<SimdLevel>(out_type, options)); + return Status::OK(); + } + + template <typename Type> + enable_if_number<Type, Status> Visit(const Type&) { + state.reset(new MinMaxImpl<Type, SimdLevel>(out_type, options)); + return Status::OK(); + } + Result<std::unique_ptr<KernelState>> Create() { RETURN_NOT_OK(VisitTypeInline(in_type, this)); - return std::move(state); - } -}; - -} // namespace aggregate -} // namespace compute -} // namespace arrow + return std::move(state); + } +}; + +} // namespace aggregate +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_internal.h index 930242ac92..ed29f26f2c 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_internal.h @@ -1,54 +1,54 @@ -// 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 "arrow/type.h" -#include "arrow/type_traits.h" +// 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 "arrow/type.h" +#include "arrow/type_traits.h" #include "arrow/util/bit_run_reader.h" #include "arrow/util/logging.h" - -namespace arrow { -namespace compute { - -// Find the largest compatible primitive type for a primitive type. -template <typename I, typename Enable = void> -struct FindAccumulatorType {}; - -template <typename I> -struct FindAccumulatorType<I, enable_if_boolean<I>> { - using Type = UInt64Type; -}; - -template <typename I> -struct FindAccumulatorType<I, enable_if_signed_integer<I>> { - using Type = Int64Type; -}; - -template <typename I> -struct FindAccumulatorType<I, enable_if_unsigned_integer<I>> { - using Type = UInt64Type; -}; - -template <typename I> -struct FindAccumulatorType<I, enable_if_floating_point<I>> { - using Type = DoubleType; -}; - + +namespace arrow { +namespace compute { + +// Find the largest compatible primitive type for a primitive type. +template <typename I, typename Enable = void> +struct FindAccumulatorType {}; + +template <typename I> +struct FindAccumulatorType<I, enable_if_boolean<I>> { + using Type = UInt64Type; +}; + +template <typename I> +struct FindAccumulatorType<I, enable_if_signed_integer<I>> { + using Type = Int64Type; +}; + +template <typename I> +struct FindAccumulatorType<I, enable_if_unsigned_integer<I>> { + using Type = UInt64Type; +}; + +template <typename I> +struct FindAccumulatorType<I, enable_if_floating_point<I>> { + using Type = DoubleType; +}; + struct ScalarAggregator : public KernelState { virtual Status Consume(KernelContext* ctx, const ExecBatch& batch) = 0; virtual Status MergeFrom(KernelContext* ctx, KernelState&& src) = 0; @@ -168,5 +168,5 @@ SumType SumArray(const ArrayData& data) { } // namespace detail -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_mode.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_mode.cc index 4d8f0fc42d..6ad0eeb645 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_mode.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_mode.cc @@ -1,24 +1,24 @@ -// 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. - -#include <cmath> +// 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. + +#include <cmath> #include <queue> #include <utility> - + #include "arrow/compute/api_aggregate.h" #include "arrow/compute/kernels/aggregate_internal.h" #include "arrow/compute/kernels/common.h" @@ -26,31 +26,31 @@ #include "arrow/result.h" #include "arrow/stl_allocator.h" #include "arrow/type_traits.h" - -namespace arrow { -namespace compute { + +namespace arrow { +namespace compute { namespace internal { - -namespace { - + +namespace { + using ModeState = OptionsWrapper<ModeOptions>; - + constexpr char kModeFieldName[] = "mode"; constexpr char kCountFieldName[] = "count"; - + constexpr uint64_t kCountEOF = ~0ULL; - + template <typename InType, typename CType = typename InType::c_type> Result<std::pair<CType*, int64_t*>> PrepareOutput(int64_t n, KernelContext* ctx, Datum* out) { const auto& mode_type = TypeTraits<InType>::type_singleton(); const auto& count_type = int64(); - + auto mode_data = ArrayData::Make(mode_type, /*length=*/n, /*null_count=*/0); mode_data->buffers.resize(2, nullptr); auto count_data = ArrayData::Make(count_type, n, 0); count_data->buffers.resize(2, nullptr); - + CType* mode_buffer = nullptr; int64_t* count_buffer = nullptr; @@ -59,28 +59,28 @@ Result<std::pair<CType*, int64_t*>> PrepareOutput(int64_t n, KernelContext* ctx, ARROW_ASSIGN_OR_RAISE(count_data->buffers[1], ctx->Allocate(n * sizeof(int64_t))); mode_buffer = mode_data->template GetMutableValues<CType>(1); count_buffer = count_data->template GetMutableValues<int64_t>(1); - } - + } + const auto& out_type = struct_({field(kModeFieldName, mode_type), field(kCountFieldName, count_type)}); *out = Datum(ArrayData::Make(out_type, n, {nullptr}, {mode_data, count_data}, 0)); return std::make_pair(mode_buffer, count_buffer); -} - +} + // find top-n value:count pairs with minimal heap // suboptimal for tiny or large n, possibly okay as we're not in hot path template <typename InType, typename Generator> Status Finalize(KernelContext* ctx, Datum* out, Generator&& gen) { using CType = typename InType::c_type; - + using ValueCountPair = std::pair<CType, uint64_t>; auto gt = [](const ValueCountPair& lhs, const ValueCountPair& rhs) { const bool rhs_is_nan = rhs.first != rhs.first; // nan as largest value return lhs.second > rhs.second || (lhs.second == rhs.second && (lhs.first < rhs.first || rhs_is_nan)); }; - + std::priority_queue<ValueCountPair, std::vector<ValueCountPair>, decltype(gt)> min_heap( std::move(gt)); @@ -94,10 +94,10 @@ Status Finalize(KernelContext* ctx, Datum* out, Generator&& gen) { } else if (gt(value_count, min_heap.top())) { min_heap.pop(); min_heap.push(value_count); - } - } + } + } const int64_t n = min_heap.size(); - + CType* mode_buffer; int64_t* count_buffer; ARROW_ASSIGN_OR_RAISE(std::tie(mode_buffer, count_buffer), @@ -109,29 +109,29 @@ Status Finalize(KernelContext* ctx, Datum* out, Generator&& gen) { } return Status::OK(); -} - +} + // count value occurances for integers with narrow value range // O(1) space, O(n) time template <typename T> struct CountModer { using CType = typename T::c_type; - + CType min; std::vector<uint64_t> counts; - + CountModer(CType min, CType max) { uint32_t value_range = static_cast<uint32_t>(max - min) + 1; DCHECK_LT(value_range, 1 << 20); this->min = min; this->counts.resize(value_range, 0); - } - + } + Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { // count values in all chunks, ignore nulls const Datum& datum = batch[0]; CountValues<CType>(this->counts.data(), datum, this->min); - + // generator to emit next value:count pair int index = 0; auto gen = [&]() { @@ -145,17 +145,17 @@ struct CountModer { } return std::pair<CType, uint64_t>(0, kCountEOF); }; - + return Finalize<T>(ctx, out, std::move(gen)); } }; - + // booleans can be handled more straightforward template <> struct CountModer<BooleanType> { Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { int64_t counts[2]{}; - + const Datum& datum = batch[0]; for (const auto& array : datum.chunks()) { if (array->length() > array->null_count()) { @@ -164,13 +164,13 @@ struct CountModer<BooleanType> { const int64_t false_count = array->length() - array->null_count() - true_count; counts[true] += true_count; counts[false] += false_count; - } - } - + } + } + const ModeOptions& options = ModeState::Get(ctx); const int64_t distinct_values = (counts[0] != 0) + (counts[1] != 0); const int64_t n = std::min(options.n, distinct_values); - + bool* mode_buffer; int64_t* count_buffer; ARROW_ASSIGN_OR_RAISE(std::tie(mode_buffer, count_buffer), @@ -183,31 +183,31 @@ struct CountModer<BooleanType> { if (n == 2) { mode_buffer[1] = !index; count_buffer[1] = counts[!index]; - } - } + } + } return Status::OK(); - } -}; - + } +}; + // copy and sort approach for floating points or integers with wide value range // O(n) space, O(nlogn) time template <typename T> struct SortModer { using CType = typename T::c_type; using Allocator = arrow::stl::allocator<CType>; - + Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { // copy all chunks to a buffer, ignore nulls and nans std::vector<CType, Allocator> in_buffer(Allocator(ctx->memory_pool())); - + uint64_t nan_count = 0; const Datum& datum = batch[0]; const int64_t in_length = datum.length() - datum.null_count(); if (in_length > 0) { in_buffer.resize(in_length); CopyNonNullValues(datum, in_buffer.data()); - + // drop nan if (is_floating_type<T>::value) { const auto& it = std::remove_if(in_buffer.begin(), in_buffer.end(), @@ -243,14 +243,14 @@ struct SortModer { }; return Finalize<T>(ctx, out, std::move(gen)); - } + } }; - + // pick counting or sorting approach per integers value range template <typename T> struct CountOrSortModer { using CType = typename T::c_type; - + Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { // cross point to benefit from counting approach // about 2x improvement for int32/64 from micro-benchmarking @@ -265,12 +265,12 @@ struct CountOrSortModer { if (static_cast<uint64_t>(max) - static_cast<uint64_t>(min) <= kMaxValueRange) { return CountModer<T>(min, max).Exec(ctx, batch, out); } - } + } return SortModer<T>().Exec(ctx, batch, out); - } + } }; - + template <typename InType, typename Enable = void> struct Moder; @@ -278,30 +278,30 @@ template <> struct Moder<Int8Type> { CountModer<Int8Type> impl; Moder() : impl(-128, 127) {} -}; - +}; + template <> struct Moder<UInt8Type> { CountModer<UInt8Type> impl; Moder() : impl(0, 255) {} }; - + template <> struct Moder<BooleanType> { CountModer<BooleanType> impl; }; - + template <typename InType> struct Moder<InType, enable_if_t<(is_integer_type<InType>::value && (sizeof(typename InType::c_type) > 1))>> { CountOrSortModer<InType> impl; }; - + template <typename InType> struct Moder<InType, enable_if_t<is_floating_type<InType>::value>> { SortModer<InType> impl; }; - + template <typename T> Status ScalarMode(KernelContext* ctx, const Scalar& scalar, Datum* out) { using CType = typename T::c_type; @@ -314,12 +314,12 @@ Status ScalarMode(KernelContext* ctx, const Scalar& scalar, Datum* out) { } return std::pair<CType, uint64_t>(static_cast<CType>(0), kCountEOF); }); - } + } return Finalize<T>(ctx, out, []() { return std::pair<CType, uint64_t>(static_cast<CType>(0), kCountEOF); }); } - + template <typename _, typename InType> struct ModeExecutor { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { @@ -336,9 +336,9 @@ struct ModeExecutor { } return Moder<InType>().impl.Exec(ctx, batch, out); - } -}; - + } +}; + VectorKernel NewModeKernel(const std::shared_ptr<DataType>& in_type) { VectorKernel kernel; kernel.init = ModeState::Init; @@ -349,8 +349,8 @@ VectorKernel NewModeKernel(const std::shared_ptr<DataType>& in_type) { kernel.signature = KernelSignature::Make({InputType(in_type)}, ValueDescr::Array(out_type)); return kernel; -} - +} + void AddBooleanModeKernel(VectorFunction* func) { VectorKernel kernel = NewModeKernel(boolean()); kernel.exec = ModeExecutor<StructType, BooleanType>::Exec; @@ -362,9 +362,9 @@ void AddNumericModeKernels(VectorFunction* func) { VectorKernel kernel = NewModeKernel(type); kernel.exec = GenerateNumeric<ModeExecutor, StructType>(*type); DCHECK_OK(func->AddKernel(kernel)); - } -} - + } +} + const FunctionDoc mode_doc{ "Calculate the modal (most common) values of a numeric array", ("Returns top-n most common values and number of times they occur in an array.\n" @@ -376,8 +376,8 @@ const FunctionDoc mode_doc{ {"array"}, "ModeOptions"}; -} // namespace - +} // namespace + void RegisterScalarAggregateMode(FunctionRegistry* registry) { static auto default_options = ModeOptions::Defaults(); auto func = std::make_shared<VectorFunction>("mode", Arity::Unary(), &mode_doc, @@ -385,8 +385,8 @@ void RegisterScalarAggregateMode(FunctionRegistry* registry) { AddBooleanModeKernel(func.get()); AddNumericModeKernels(func.get()); DCHECK_OK(registry->AddFunction(std::move(func))); -} - +} + } // namespace internal -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_var_std.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_var_std.cc index 82fc3a2752..d6965fed4a 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_var_std.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/aggregate_var_std.cc @@ -1,70 +1,70 @@ -// 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. - +// 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. + #include <cmath> - + #include "arrow/compute/api_aggregate.h" #include "arrow/compute/kernels/aggregate_internal.h" #include "arrow/compute/kernels/common.h" #include "arrow/util/bit_run_reader.h" #include "arrow/util/int128_internal.h" -namespace arrow { -namespace compute { +namespace arrow { +namespace compute { namespace internal { - -namespace { - + +namespace { + using arrow::internal::int128_t; using arrow::internal::VisitSetBitRunsVoid; -template <typename ArrowType> -struct VarStdState { - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; +template <typename ArrowType> +struct VarStdState { + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; using CType = typename ArrowType::c_type; - using ThisType = VarStdState<ArrowType>; - + using ThisType = VarStdState<ArrowType>; + // float/double/int64: calculate `m2` (sum((X-mean)^2)) with `two pass algorithm` - // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm + // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm template <typename T = ArrowType> enable_if_t<is_floating_type<T>::value || (sizeof(CType) > 4)> Consume( const ArrayType& array) { - int64_t count = array.length() - array.null_count(); - if (count == 0) { - return; - } - + int64_t count = array.length() - array.null_count(); + if (count == 0) { + return; + } + using SumType = typename std::conditional<is_floating_type<T>::value, double, int128_t>::type; SumType sum = arrow::compute::detail::SumArray<CType, SumType, SimdLevel::NONE>(*array.data()); - + const double mean = static_cast<double>(sum) / count; const double m2 = arrow::compute::detail::SumArray<CType, double, SimdLevel::NONE>( *array.data(), [mean](CType value) { const double v = static_cast<double>(value); return (v - mean) * (v - mean); }); - - this->count = count; + + this->count = count; this->mean = mean; - this->m2 = m2; - } - + this->m2 = m2; + } + // int32/16/8: textbook one pass algorithm with integer arithmetic template <typename T = ArrowType> enable_if_t<is_integer_type<T>::value && (sizeof(CType) <= 4)> Consume( @@ -118,69 +118,69 @@ struct VarStdState { // Combine `m2` from two chunks (m2 = n*s2) // https://www.emathzone.com/tutorials/basic-statistics/combined-variance.html - void MergeFrom(const ThisType& state) { - if (state.count == 0) { - return; - } - if (this->count == 0) { - this->count = state.count; + void MergeFrom(const ThisType& state) { + if (state.count == 0) { + return; + } + if (this->count == 0) { + this->count = state.count; this->mean = state.mean; - this->m2 = state.m2; - return; - } + this->m2 = state.m2; + return; + } double mean = (this->mean * this->count + state.mean * state.count) / (this->count + state.count); this->m2 += state.m2 + this->count * (this->mean - mean) * (this->mean - mean) + state.count * (state.mean - mean) * (state.mean - mean); - this->count += state.count; + this->count += state.count; this->mean = mean; - } - - int64_t count = 0; + } + + int64_t count = 0; double mean = 0; double m2 = 0; // m2 = count*s2 = sum((X-mean)^2) -}; - -enum class VarOrStd : bool { Var, Std }; - -template <typename ArrowType> -struct VarStdImpl : public ScalarAggregator { - using ThisType = VarStdImpl<ArrowType>; - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; - - explicit VarStdImpl(const std::shared_ptr<DataType>& out_type, - const VarianceOptions& options, VarOrStd return_type) - : out_type(out_type), options(options), return_type(return_type) {} - +}; + +enum class VarOrStd : bool { Var, Std }; + +template <typename ArrowType> +struct VarStdImpl : public ScalarAggregator { + using ThisType = VarStdImpl<ArrowType>; + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; + + explicit VarStdImpl(const std::shared_ptr<DataType>& out_type, + const VarianceOptions& options, VarOrStd return_type) + : out_type(out_type), options(options), return_type(return_type) {} + Status Consume(KernelContext*, const ExecBatch& batch) override { - ArrayType array(batch[0].array()); - this->state.Consume(array); + ArrayType array(batch[0].array()); + this->state.Consume(array); return Status::OK(); - } - + } + Status MergeFrom(KernelContext*, KernelState&& src) override { - const auto& other = checked_cast<const ThisType&>(src); - this->state.MergeFrom(other.state); + const auto& other = checked_cast<const ThisType&>(src); + this->state.MergeFrom(other.state); return Status::OK(); - } - + } + Status Finalize(KernelContext*, Datum* out) override { - if (this->state.count <= options.ddof) { + if (this->state.count <= options.ddof) { out->value = std::make_shared<DoubleScalar>(); - } else { - double var = this->state.m2 / (this->state.count - options.ddof); - out->value = + } else { + double var = this->state.m2 / (this->state.count - options.ddof); + out->value = std::make_shared<DoubleScalar>(return_type == VarOrStd::Var ? var : sqrt(var)); - } + } return Status::OK(); - } - - std::shared_ptr<DataType> out_type; - VarStdState<ArrowType> state; - VarianceOptions options; - VarOrStd return_type; -}; - + } + + std::shared_ptr<DataType> out_type; + VarStdState<ArrowType> state; + VarianceOptions options; + VarOrStd return_type; +}; + struct ScalarVarStdImpl : public ScalarAggregator { explicit ScalarVarStdImpl(const VarianceOptions& options) : options(options), seen(false) {} @@ -209,77 +209,77 @@ struct ScalarVarStdImpl : public ScalarAggregator { bool seen; }; -struct VarStdInitState { - std::unique_ptr<KernelState> state; - KernelContext* ctx; - const DataType& in_type; - const std::shared_ptr<DataType>& out_type; - const VarianceOptions& options; - VarOrStd return_type; - - VarStdInitState(KernelContext* ctx, const DataType& in_type, - const std::shared_ptr<DataType>& out_type, - const VarianceOptions& options, VarOrStd return_type) - : ctx(ctx), - in_type(in_type), - out_type(out_type), - options(options), - return_type(return_type) {} - - Status Visit(const DataType&) { - return Status::NotImplemented("No variance/stddev implemented"); - } - - Status Visit(const HalfFloatType&) { - return Status::NotImplemented("No variance/stddev implemented"); - } - - template <typename Type> - enable_if_t<is_number_type<Type>::value, Status> Visit(const Type&) { - state.reset(new VarStdImpl<Type>(out_type, options, return_type)); - return Status::OK(); - } - +struct VarStdInitState { + std::unique_ptr<KernelState> state; + KernelContext* ctx; + const DataType& in_type; + const std::shared_ptr<DataType>& out_type; + const VarianceOptions& options; + VarOrStd return_type; + + VarStdInitState(KernelContext* ctx, const DataType& in_type, + const std::shared_ptr<DataType>& out_type, + const VarianceOptions& options, VarOrStd return_type) + : ctx(ctx), + in_type(in_type), + out_type(out_type), + options(options), + return_type(return_type) {} + + Status Visit(const DataType&) { + return Status::NotImplemented("No variance/stddev implemented"); + } + + Status Visit(const HalfFloatType&) { + return Status::NotImplemented("No variance/stddev implemented"); + } + + template <typename Type> + enable_if_t<is_number_type<Type>::value, Status> Visit(const Type&) { + state.reset(new VarStdImpl<Type>(out_type, options, return_type)); + return Status::OK(); + } + Result<std::unique_ptr<KernelState>> Create() { RETURN_NOT_OK(VisitTypeInline(in_type, this)); - return std::move(state); - } -}; - + return std::move(state); + } +}; + Result<std::unique_ptr<KernelState>> StddevInit(KernelContext* ctx, const KernelInitArgs& args) { - VarStdInitState visitor( - ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), - static_cast<const VarianceOptions&>(*args.options), VarOrStd::Std); - return visitor.Create(); -} - + VarStdInitState visitor( + ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), + static_cast<const VarianceOptions&>(*args.options), VarOrStd::Std); + return visitor.Create(); +} + Result<std::unique_ptr<KernelState>> VarianceInit(KernelContext* ctx, const KernelInitArgs& args) { - VarStdInitState visitor( - ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), - static_cast<const VarianceOptions&>(*args.options), VarOrStd::Var); - return visitor.Create(); -} - + VarStdInitState visitor( + ctx, *args.inputs[0].type, args.kernel->signature->out_type().type(), + static_cast<const VarianceOptions&>(*args.options), VarOrStd::Var); + return visitor.Create(); +} + Result<std::unique_ptr<KernelState>> ScalarVarStdInit(KernelContext* ctx, const KernelInitArgs& args) { return arrow::internal::make_unique<ScalarVarStdImpl>( static_cast<const VarianceOptions&>(*args.options)); } -void AddVarStdKernels(KernelInit init, - const std::vector<std::shared_ptr<DataType>>& types, - ScalarAggregateFunction* func) { - for (const auto& ty : types) { - auto sig = KernelSignature::Make({InputType::Array(ty)}, float64()); - AddAggKernel(std::move(sig), init, func); +void AddVarStdKernels(KernelInit init, + const std::vector<std::shared_ptr<DataType>>& types, + ScalarAggregateFunction* func) { + for (const auto& ty : types) { + auto sig = KernelSignature::Make({InputType::Array(ty)}, float64()); + AddAggKernel(std::move(sig), init, func); sig = KernelSignature::Make({InputType::Scalar(ty)}, float64()); AddAggKernel(std::move(sig), ScalarVarStdInit, func); - } -} - + } +} + const FunctionDoc stddev_doc{ "Calculate the standard deviation of a numeric array", ("The number of degrees of freedom can be controlled using VarianceOptions.\n" @@ -288,7 +288,7 @@ const FunctionDoc stddev_doc{ "to satisfy `ddof`, null is returned."), {"array"}, "VarianceOptions"}; - + const FunctionDoc variance_doc{ "Calculate the variance of a numeric array", ("The number of degrees of freedom can be controlled using VarianceOptions.\n" @@ -298,22 +298,22 @@ const FunctionDoc variance_doc{ {"array"}, "VarianceOptions"}; -std::shared_ptr<ScalarAggregateFunction> AddStddevAggKernels() { - static auto default_std_options = VarianceOptions::Defaults(); +std::shared_ptr<ScalarAggregateFunction> AddStddevAggKernels() { + static auto default_std_options = VarianceOptions::Defaults(); auto func = std::make_shared<ScalarAggregateFunction>( "stddev", Arity::Unary(), &stddev_doc, &default_std_options); AddVarStdKernels(StddevInit, NumericTypes(), func.get()); - return func; -} - -std::shared_ptr<ScalarAggregateFunction> AddVarianceAggKernels() { - static auto default_var_options = VarianceOptions::Defaults(); + return func; +} + +std::shared_ptr<ScalarAggregateFunction> AddVarianceAggKernels() { + static auto default_var_options = VarianceOptions::Defaults(); auto func = std::make_shared<ScalarAggregateFunction>( "variance", Arity::Unary(), &variance_doc, &default_var_options); AddVarStdKernels(VarianceInit, NumericTypes(), func.get()); - return func; -} - + return func; +} + } // namespace void RegisterScalarAggregateVariance(FunctionRegistry* registry) { @@ -322,5 +322,5 @@ void RegisterScalarAggregateVariance(FunctionRegistry* registry) { } } // namespace internal -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.cc index aa342eec25..bab8e7000c 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.cc @@ -1,195 +1,195 @@ -// 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. - -#include "arrow/compute/kernels/codegen_internal.h" - -#include <functional> -#include <memory> -#include <mutex> -#include <vector> - -#include "arrow/type_fwd.h" - -namespace arrow { -namespace compute { -namespace internal { - +// 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. + +#include "arrow/compute/kernels/codegen_internal.h" + +#include <functional> +#include <memory> +#include <mutex> +#include <vector> + +#include "arrow/type_fwd.h" + +namespace arrow { +namespace compute { +namespace internal { + Status ExecFail(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return Status::NotImplemented("This kernel is malformed"); -} - -ArrayKernelExec MakeFlippedBinaryExec(ArrayKernelExec exec) { - return [exec](KernelContext* ctx, const ExecBatch& batch, Datum* out) { - ExecBatch flipped_batch = batch; - std::swap(flipped_batch.values[0], flipped_batch.values[1]); +} + +ArrayKernelExec MakeFlippedBinaryExec(ArrayKernelExec exec) { + return [exec](KernelContext* ctx, const ExecBatch& batch, Datum* out) { + ExecBatch flipped_batch = batch; + std::swap(flipped_batch.values[0], flipped_batch.values[1]); return exec(ctx, flipped_batch, out); - }; -} - -std::vector<std::shared_ptr<DataType>> g_signed_int_types; -std::vector<std::shared_ptr<DataType>> g_unsigned_int_types; -std::vector<std::shared_ptr<DataType>> g_int_types; -std::vector<std::shared_ptr<DataType>> g_floating_types; -std::vector<std::shared_ptr<DataType>> g_numeric_types; -std::vector<std::shared_ptr<DataType>> g_base_binary_types; -std::vector<std::shared_ptr<DataType>> g_temporal_types; -std::vector<std::shared_ptr<DataType>> g_primitive_types; + }; +} + +std::vector<std::shared_ptr<DataType>> g_signed_int_types; +std::vector<std::shared_ptr<DataType>> g_unsigned_int_types; +std::vector<std::shared_ptr<DataType>> g_int_types; +std::vector<std::shared_ptr<DataType>> g_floating_types; +std::vector<std::shared_ptr<DataType>> g_numeric_types; +std::vector<std::shared_ptr<DataType>> g_base_binary_types; +std::vector<std::shared_ptr<DataType>> g_temporal_types; +std::vector<std::shared_ptr<DataType>> g_primitive_types; std::vector<Type::type> g_decimal_type_ids; -static std::once_flag codegen_static_initialized; - -template <typename T> -void Extend(const std::vector<T>& values, std::vector<T>* out) { - for (const auto& t : values) { - out->push_back(t); - } -} - -static void InitStaticData() { - // Signed int types - g_signed_int_types = {int8(), int16(), int32(), int64()}; - - // Unsigned int types - g_unsigned_int_types = {uint8(), uint16(), uint32(), uint64()}; - - // All int types - Extend(g_unsigned_int_types, &g_int_types); - Extend(g_signed_int_types, &g_int_types); - - // Floating point types - g_floating_types = {float32(), float64()}; - +static std::once_flag codegen_static_initialized; + +template <typename T> +void Extend(const std::vector<T>& values, std::vector<T>* out) { + for (const auto& t : values) { + out->push_back(t); + } +} + +static void InitStaticData() { + // Signed int types + g_signed_int_types = {int8(), int16(), int32(), int64()}; + + // Unsigned int types + g_unsigned_int_types = {uint8(), uint16(), uint32(), uint64()}; + + // All int types + Extend(g_unsigned_int_types, &g_int_types); + Extend(g_signed_int_types, &g_int_types); + + // Floating point types + g_floating_types = {float32(), float64()}; + // Decimal types g_decimal_type_ids = {Type::DECIMAL128, Type::DECIMAL256}; - // Numeric types - Extend(g_int_types, &g_numeric_types); - Extend(g_floating_types, &g_numeric_types); - - // Temporal types - g_temporal_types = {date32(), - date64(), - time32(TimeUnit::SECOND), - time32(TimeUnit::MILLI), - time64(TimeUnit::MICRO), - time64(TimeUnit::NANO), - timestamp(TimeUnit::SECOND), - timestamp(TimeUnit::MILLI), - timestamp(TimeUnit::MICRO), - timestamp(TimeUnit::NANO)}; - - // Base binary types (without FixedSizeBinary) - g_base_binary_types = {binary(), utf8(), large_binary(), large_utf8()}; - - // Non-parametric, non-nested types. This also DOES NOT include - // - // * Decimal - // * Fixed Size Binary - // * Time32 - // * Time64 - // * Timestamp - g_primitive_types = {null(), boolean(), date32(), date64()}; - Extend(g_numeric_types, &g_primitive_types); - Extend(g_base_binary_types, &g_primitive_types); -} - -const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_base_binary_types; -} - -const std::vector<std::shared_ptr<DataType>>& StringTypes() { - static DataTypeVector types = {utf8(), large_utf8()}; - return types; -} - -const std::vector<std::shared_ptr<DataType>>& SignedIntTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_signed_int_types; -} - -const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_unsigned_int_types; -} - -const std::vector<std::shared_ptr<DataType>>& IntTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_int_types; -} - -const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_floating_types; -} - + // Numeric types + Extend(g_int_types, &g_numeric_types); + Extend(g_floating_types, &g_numeric_types); + + // Temporal types + g_temporal_types = {date32(), + date64(), + time32(TimeUnit::SECOND), + time32(TimeUnit::MILLI), + time64(TimeUnit::MICRO), + time64(TimeUnit::NANO), + timestamp(TimeUnit::SECOND), + timestamp(TimeUnit::MILLI), + timestamp(TimeUnit::MICRO), + timestamp(TimeUnit::NANO)}; + + // Base binary types (without FixedSizeBinary) + g_base_binary_types = {binary(), utf8(), large_binary(), large_utf8()}; + + // Non-parametric, non-nested types. This also DOES NOT include + // + // * Decimal + // * Fixed Size Binary + // * Time32 + // * Time64 + // * Timestamp + g_primitive_types = {null(), boolean(), date32(), date64()}; + Extend(g_numeric_types, &g_primitive_types); + Extend(g_base_binary_types, &g_primitive_types); +} + +const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_base_binary_types; +} + +const std::vector<std::shared_ptr<DataType>>& StringTypes() { + static DataTypeVector types = {utf8(), large_utf8()}; + return types; +} + +const std::vector<std::shared_ptr<DataType>>& SignedIntTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_signed_int_types; +} + +const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_unsigned_int_types; +} + +const std::vector<std::shared_ptr<DataType>>& IntTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_int_types; +} + +const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_floating_types; +} + const std::vector<Type::type>& DecimalTypeIds() { std::call_once(codegen_static_initialized, InitStaticData); return g_decimal_type_ids; } -const std::vector<TimeUnit::type>& AllTimeUnits() { - static std::vector<TimeUnit::type> units = {TimeUnit::SECOND, TimeUnit::MILLI, - TimeUnit::MICRO, TimeUnit::NANO}; - return units; -} - -const std::vector<std::shared_ptr<DataType>>& NumericTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_numeric_types; -} - -const std::vector<std::shared_ptr<DataType>>& TemporalTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_temporal_types; -} - -const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes() { - std::call_once(codegen_static_initialized, InitStaticData); - return g_primitive_types; -} - -const std::vector<std::shared_ptr<DataType>>& ExampleParametricTypes() { - static DataTypeVector example_parametric_types = { +const std::vector<TimeUnit::type>& AllTimeUnits() { + static std::vector<TimeUnit::type> units = {TimeUnit::SECOND, TimeUnit::MILLI, + TimeUnit::MICRO, TimeUnit::NANO}; + return units; +} + +const std::vector<std::shared_ptr<DataType>>& NumericTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_numeric_types; +} + +const std::vector<std::shared_ptr<DataType>>& TemporalTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_temporal_types; +} + +const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes() { + std::call_once(codegen_static_initialized, InitStaticData); + return g_primitive_types; +} + +const std::vector<std::shared_ptr<DataType>>& ExampleParametricTypes() { + static DataTypeVector example_parametric_types = { decimal128(12, 2), - duration(TimeUnit::SECOND), - timestamp(TimeUnit::SECOND), - time32(TimeUnit::SECOND), - time64(TimeUnit::MICRO), - fixed_size_binary(0), - list(null()), - large_list(null()), - fixed_size_list(field("dummy", null()), 0), - struct_({}), - sparse_union(FieldVector{}), - dense_union(FieldVector{}), - dictionary(int32(), null()), - map(null(), null())}; - return example_parametric_types; -} - -// Construct dummy parametric types so that we can get VisitTypeInline to -// work above - -Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs) { + duration(TimeUnit::SECOND), + timestamp(TimeUnit::SECOND), + time32(TimeUnit::SECOND), + time64(TimeUnit::MICRO), + fixed_size_binary(0), + list(null()), + large_list(null()), + fixed_size_list(field("dummy", null()), 0), + struct_({}), + sparse_union(FieldVector{}), + dense_union(FieldVector{}), + dictionary(int32(), null()), + map(null(), null())}; + return example_parametric_types; +} + +// Construct dummy parametric types so that we can get VisitTypeInline to +// work above + +Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs) { ValueDescr result = descrs.front(); result.shape = GetBroadcastShape(descrs); return result; -} - +} + void EnsureDictionaryDecoded(std::vector<ValueDescr>* descrs) { for (ValueDescr& descr : *descrs) { if (descr.type->id() == Type::DICTIONARY) { @@ -332,6 +332,6 @@ std::shared_ptr<DataType> CommonBinary(const std::vector<ValueDescr>& descrs) { return large_binary(); } -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.h index 11a08a6ea9..cb9b13bb3d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/codegen_internal.h @@ -1,95 +1,95 @@ -// 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 <cstdint> +// 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 <cstdint> #include <cstring> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "arrow/array/builder_binary.h" -#include "arrow/array/data.h" -#include "arrow/buffer.h" -#include "arrow/buffer_builder.h" -#include "arrow/compute/exec.h" -#include "arrow/compute/kernel.h" -#include "arrow/datum.h" -#include "arrow/result.h" -#include "arrow/scalar.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_traits.h" -#include "arrow/util/bit_block_counter.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_generate.h" -#include "arrow/util/bitmap_reader.h" -#include "arrow/util/bitmap_writer.h" -#include "arrow/util/checked_cast.h" -#include "arrow/util/decimal.h" -#include "arrow/util/logging.h" -#include "arrow/util/macros.h" -#include "arrow/util/make_unique.h" -#include "arrow/util/optional.h" -#include "arrow/util/string_view.h" -#include "arrow/visitor_inline.h" - -namespace arrow { - -using internal::BinaryBitBlockCounter; -using internal::BitBlockCount; -using internal::BitmapReader; -using internal::checked_cast; -using internal::FirstTimeBitmapWriter; -using internal::GenerateBitsUnrolled; -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 <typename OptionsType> -struct OptionsWrapper : public KernelState { - explicit OptionsWrapper(OptionsType options) : options(std::move(options)) {} - +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "arrow/array/builder_binary.h" +#include "arrow/array/data.h" +#include "arrow/buffer.h" +#include "arrow/buffer_builder.h" +#include "arrow/compute/exec.h" +#include "arrow/compute/kernel.h" +#include "arrow/datum.h" +#include "arrow/result.h" +#include "arrow/scalar.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_traits.h" +#include "arrow/util/bit_block_counter.h" +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_generate.h" +#include "arrow/util/bitmap_reader.h" +#include "arrow/util/bitmap_writer.h" +#include "arrow/util/checked_cast.h" +#include "arrow/util/decimal.h" +#include "arrow/util/logging.h" +#include "arrow/util/macros.h" +#include "arrow/util/make_unique.h" +#include "arrow/util/optional.h" +#include "arrow/util/string_view.h" +#include "arrow/visitor_inline.h" + +namespace arrow { + +using internal::BinaryBitBlockCounter; +using internal::BitBlockCount; +using internal::BitmapReader; +using internal::checked_cast; +using internal::FirstTimeBitmapWriter; +using internal::GenerateBitsUnrolled; +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 <typename OptionsType> +struct OptionsWrapper : public KernelState { + explicit OptionsWrapper(OptionsType options) : options(std::move(options)) {} + static Result<std::unique_ptr<KernelState>> Init(KernelContext* ctx, const KernelInitArgs& args) { - if (auto options = static_cast<const OptionsType*>(args.options)) { - return ::arrow::internal::make_unique<OptionsWrapper>(*options); - } - + if (auto options = static_cast<const OptionsType*>(args.options)) { + return ::arrow::internal::make_unique<OptionsWrapper>(*options); + } + return Status::Invalid( "Attempted to initialize KernelState from null FunctionOptions"); - } - - static const OptionsType& Get(const KernelState& state) { - return ::arrow::internal::checked_cast<const OptionsWrapper&>(state).options; - } - - static const OptionsType& Get(KernelContext* ctx) { return Get(*ctx->state()); } - - OptionsType options; -}; - + } + + static const OptionsType& Get(const KernelState& state) { + return ::arrow::internal::checked_cast<const OptionsWrapper&>(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 <typename StateType, typename OptionsType> @@ -118,41 +118,41 @@ struct KernelStateFromFunctionOptions : public KernelState { StateType state; }; -// ---------------------------------------------------------------------- -// Input and output value type definitions - -template <typename Type, typename Enable = void> -struct GetViewType; - -template <typename Type> -struct GetViewType<Type, enable_if_has_c_type<Type>> { - using T = typename Type::c_type; - using PhysicalType = T; - - static T LogicalValue(PhysicalType value) { return value; } -}; - -template <typename Type> -struct GetViewType<Type, enable_if_t<is_base_binary_type<Type>::value || - is_fixed_size_binary_type<Type>::value>> { - using T = util::string_view; - using PhysicalType = T; - - static T LogicalValue(PhysicalType value) { return value; } -}; - -template <> -struct GetViewType<Decimal128Type> { - using T = Decimal128; - using PhysicalType = util::string_view; - - static T LogicalValue(PhysicalType value) { - return Decimal128(reinterpret_cast<const uint8_t*>(value.data())); - } +// ---------------------------------------------------------------------- +// Input and output value type definitions + +template <typename Type, typename Enable = void> +struct GetViewType; + +template <typename Type> +struct GetViewType<Type, enable_if_has_c_type<Type>> { + using T = typename Type::c_type; + using PhysicalType = T; + + static T LogicalValue(PhysicalType value) { return value; } +}; + +template <typename Type> +struct GetViewType<Type, enable_if_t<is_base_binary_type<Type>::value || + is_fixed_size_binary_type<Type>::value>> { + using T = util::string_view; + using PhysicalType = T; + + static T LogicalValue(PhysicalType value) { return value; } +}; + +template <> +struct GetViewType<Decimal128Type> { + using T = Decimal128; + using PhysicalType = util::string_view; + + static T LogicalValue(PhysicalType value) { + return Decimal128(reinterpret_cast<const uint8_t*>(value.data())); + } static T LogicalValue(T value) { return value; } -}; - +}; + template <> struct GetViewType<Decimal256Type> { using T = Decimal256; @@ -165,88 +165,88 @@ struct GetViewType<Decimal256Type> { static T LogicalValue(T value) { return value; } }; -template <typename Type, typename Enable = void> -struct GetOutputType; - -template <typename Type> -struct GetOutputType<Type, enable_if_has_c_type<Type>> { - using T = typename Type::c_type; -}; - -template <typename Type> -struct GetOutputType<Type, enable_if_t<is_string_like_type<Type>::value>> { - using T = std::string; -}; - -template <> -struct GetOutputType<Decimal128Type> { - using T = Decimal128; -}; - +template <typename Type, typename Enable = void> +struct GetOutputType; + +template <typename Type> +struct GetOutputType<Type, enable_if_has_c_type<Type>> { + using T = typename Type::c_type; +}; + +template <typename Type> +struct GetOutputType<Type, enable_if_t<is_string_like_type<Type>::value>> { + using T = std::string; +}; + +template <> +struct GetOutputType<Decimal128Type> { + using T = Decimal128; +}; + template <> struct GetOutputType<Decimal256Type> { using T = Decimal256; }; -// ---------------------------------------------------------------------- -// Iteration / value access utilities - -template <typename T, typename R = void> -using enable_if_has_c_type_not_boolean = - enable_if_t<has_c_type<T>::value && !is_boolean_type<T>::value, R>; - -// Iterator over various input array types, yielding a GetViewType<Type> - -template <typename Type, typename Enable = void> -struct ArrayIterator; - -template <typename Type> -struct ArrayIterator<Type, enable_if_has_c_type_not_boolean<Type>> { - using T = typename Type::c_type; - const T* values; - - explicit ArrayIterator(const ArrayData& data) : values(data.GetValues<T>(1)) {} - T operator()() { return *values++; } -}; - -template <typename Type> -struct ArrayIterator<Type, enable_if_boolean<Type>> { - BitmapReader reader; - - explicit ArrayIterator(const ArrayData& data) - : reader(data.buffers[1]->data(), data.offset, data.length) {} - bool operator()() { - bool out = reader.IsSet(); - reader.Next(); - return out; - } -}; - -template <typename Type> -struct ArrayIterator<Type, enable_if_base_binary<Type>> { - using offset_type = typename Type::offset_type; - const ArrayData& arr; - const offset_type* offsets; - offset_type cur_offset; - const char* data; - int64_t position; - - explicit ArrayIterator(const ArrayData& arr) - : arr(arr), - offsets(reinterpret_cast<const offset_type*>(arr.buffers[1]->data()) + - arr.offset), - cur_offset(offsets[0]), - data(reinterpret_cast<const char*>(arr.buffers[2]->data())), - position(0) {} - - util::string_view operator()() { - offset_type next_offset = offsets[++position]; - auto result = util::string_view(data + cur_offset, next_offset - cur_offset); - cur_offset = next_offset; - return result; - } -}; - +// ---------------------------------------------------------------------- +// Iteration / value access utilities + +template <typename T, typename R = void> +using enable_if_has_c_type_not_boolean = + enable_if_t<has_c_type<T>::value && !is_boolean_type<T>::value, R>; + +// Iterator over various input array types, yielding a GetViewType<Type> + +template <typename Type, typename Enable = void> +struct ArrayIterator; + +template <typename Type> +struct ArrayIterator<Type, enable_if_has_c_type_not_boolean<Type>> { + using T = typename Type::c_type; + const T* values; + + explicit ArrayIterator(const ArrayData& data) : values(data.GetValues<T>(1)) {} + T operator()() { return *values++; } +}; + +template <typename Type> +struct ArrayIterator<Type, enable_if_boolean<Type>> { + BitmapReader reader; + + explicit ArrayIterator(const ArrayData& data) + : reader(data.buffers[1]->data(), data.offset, data.length) {} + bool operator()() { + bool out = reader.IsSet(); + reader.Next(); + return out; + } +}; + +template <typename Type> +struct ArrayIterator<Type, enable_if_base_binary<Type>> { + using offset_type = typename Type::offset_type; + const ArrayData& arr; + const offset_type* offsets; + offset_type cur_offset; + const char* data; + int64_t position; + + explicit ArrayIterator(const ArrayData& arr) + : arr(arr), + offsets(reinterpret_cast<const offset_type*>(arr.buffers[1]->data()) + + arr.offset), + cur_offset(offsets[0]), + data(reinterpret_cast<const char*>(arr.buffers[2]->data())), + position(0) {} + + util::string_view operator()() { + offset_type next_offset = offsets[++position]; + auto result = util::string_view(data + cur_offset, next_offset - cur_offset); + cur_offset = next_offset; + return result; + } +}; + template <typename Type> struct ArrayIterator<Type, enable_if_decimal<Type>> { using T = typename TypeTraits<Type>::ScalarType::ValueType; @@ -259,27 +259,27 @@ struct ArrayIterator<Type, enable_if_decimal<Type>> { T operator()() { return T{values++->data()}; } }; -// Iterator over various output array types, taking a GetOutputType<Type> - -template <typename Type, typename Enable = void> -struct OutputArrayWriter; - -template <typename Type> -struct OutputArrayWriter<Type, enable_if_has_c_type_not_boolean<Type>> { - using T = typename Type::c_type; - T* values; - - explicit OutputArrayWriter(ArrayData* data) : values(data->GetMutableValues<T>(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{}; } +// Iterator over various output array types, taking a GetOutputType<Type> + +template <typename Type, typename Enable = void> +struct OutputArrayWriter; + +template <typename Type> +struct OutputArrayWriter<Type, enable_if_has_c_type_not_boolean<Type>> { + using T = typename Type::c_type; + T* values; + + explicit OutputArrayWriter(ArrayData* data) : values(data->GetMutableValues<T>(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(values, 0, sizeof(T) * length); } -}; - +}; + template <typename Type> struct OutputArrayWriter<Type, enable_if_decimal<Type>> { using T = typename TypeTraits<Type>::ScalarType::ValueType; @@ -296,35 +296,35 @@ struct OutputArrayWriter<Type, enable_if_decimal<Type>> { void WriteAllNull(int64_t length) { std::memset(values, 0, sizeof(T) * length); } }; -// (Un)box Scalar to / from C++ value - -template <typename Type, typename Enable = void> -struct UnboxScalar; - -template <typename Type> -struct UnboxScalar<Type, enable_if_has_c_type<Type>> { - using T = typename Type::c_type; - static T Unbox(const Scalar& val) { - return *reinterpret_cast<const T*>( - checked_cast<const ::arrow::internal::PrimitiveScalarBase&>(val).data()); - } -}; - -template <typename Type> +// (Un)box Scalar to / from C++ value + +template <typename Type, typename Enable = void> +struct UnboxScalar; + +template <typename Type> +struct UnboxScalar<Type, enable_if_has_c_type<Type>> { + using T = typename Type::c_type; + static T Unbox(const Scalar& val) { + return *reinterpret_cast<const T*>( + checked_cast<const ::arrow::internal::PrimitiveScalarBase&>(val).data()); + } +}; + +template <typename Type> struct UnboxScalar<Type, enable_if_has_string_view<Type>> { - static util::string_view Unbox(const Scalar& val) { + static util::string_view Unbox(const Scalar& val) { if (!val.is_valid) return util::string_view(); - return util::string_view(*checked_cast<const BaseBinaryScalar&>(val).value); - } -}; - -template <> -struct UnboxScalar<Decimal128Type> { - static Decimal128 Unbox(const Scalar& val) { - return checked_cast<const Decimal128Scalar&>(val).value; - } -}; - + return util::string_view(*checked_cast<const BaseBinaryScalar&>(val).value); + } +}; + +template <> +struct UnboxScalar<Decimal128Type> { + static Decimal128 Unbox(const Scalar& val) { + return checked_cast<const Decimal128Scalar&>(val).value; + } +}; + template <> struct UnboxScalar<Decimal256Type> { static Decimal256 Unbox(const Scalar& val) { @@ -332,36 +332,36 @@ struct UnboxScalar<Decimal256Type> { } }; -template <typename Type, typename Enable = void> -struct BoxScalar; - -template <typename Type> -struct BoxScalar<Type, enable_if_has_c_type<Type>> { - using T = typename GetOutputType<Type>::T; +template <typename Type, typename Enable = void> +struct BoxScalar; + +template <typename Type> +struct BoxScalar<Type, enable_if_has_c_type<Type>> { + using T = typename GetOutputType<Type>::T; static void Box(T val, Scalar* out) { // Enables BoxScalar<Int64Type> to work on a (for example) Time64Scalar T* mutable_data = reinterpret_cast<T*>( checked_cast<::arrow::internal::PrimitiveScalarBase*>(out)->mutable_data()); *mutable_data = val; } -}; - -template <typename Type> -struct BoxScalar<Type, enable_if_base_binary<Type>> { - using T = typename GetOutputType<Type>::T; - using ScalarType = typename TypeTraits<Type>::ScalarType; - static void Box(T val, Scalar* out) { - checked_cast<ScalarType*>(out)->value = std::make_shared<Buffer>(val); - } -}; - -template <> -struct BoxScalar<Decimal128Type> { - using T = Decimal128; - using ScalarType = Decimal128Scalar; - static void Box(T val, Scalar* out) { checked_cast<ScalarType*>(out)->value = val; } -}; - +}; + +template <typename Type> +struct BoxScalar<Type, enable_if_base_binary<Type>> { + using T = typename GetOutputType<Type>::T; + using ScalarType = typename TypeTraits<Type>::ScalarType; + static void Box(T val, Scalar* out) { + checked_cast<ScalarType*>(out)->value = std::make_shared<Buffer>(val); + } +}; + +template <> +struct BoxScalar<Decimal128Type> { + using T = Decimal128; + using ScalarType = Decimal128Scalar; + static void Box(T val, Scalar* out) { checked_cast<ScalarType*>(out)->value = val; } +}; + template <> struct BoxScalar<Decimal256Type> { using T = Decimal256; @@ -369,21 +369,21 @@ struct BoxScalar<Decimal256Type> { static void Box(T val, Scalar* out) { checked_cast<ScalarType*>(out)->value = val; } }; -// A VisitArrayDataInline variant that calls its visitor function with logical -// values, such as Decimal128 rather than util::string_view. - -template <typename T, typename VisitFunc, typename NullFunc> +// A VisitArrayDataInline variant that calls its visitor function with logical +// values, such as Decimal128 rather than util::string_view. + +template <typename T, typename VisitFunc, typename NullFunc> static typename arrow::internal::call_traits::enable_if_return<VisitFunc, void>::type VisitArrayValuesInline(const ArrayData& arr, VisitFunc&& valid_func, NullFunc&& null_func) { - VisitArrayDataInline<T>( - arr, - [&](typename GetViewType<T>::PhysicalType v) { - valid_func(GetViewType<T>::LogicalValue(std::move(v))); - }, - std::forward<NullFunc>(null_func)); -} - + VisitArrayDataInline<T>( + arr, + [&](typename GetViewType<T>::PhysicalType v) { + valid_func(GetViewType<T>::LogicalValue(std::move(v))); + }, + std::forward<NullFunc>(null_func)); +} + template <typename T, typename VisitFunc, typename NullFunc> static typename arrow::internal::call_traits::enable_if_return<VisitFunc, Status>::type VisitArrayValuesInline(const ArrayData& arr, VisitFunc&& valid_func, @@ -396,110 +396,110 @@ VisitArrayValuesInline(const ArrayData& arr, VisitFunc&& valid_func, std::forward<NullFunc>(null_func)); } -// Like VisitArrayValuesInline, but for binary functions. - -template <typename Arg0Type, typename Arg1Type, typename VisitFunc, typename NullFunc> -static void VisitTwoArrayValuesInline(const ArrayData& arr0, const ArrayData& arr1, - VisitFunc&& valid_func, NullFunc&& null_func) { - ArrayIterator<Arg0Type> arr0_it(arr0); - ArrayIterator<Arg1Type> arr1_it(arr1); - - auto visit_valid = [&](int64_t i) { - valid_func(GetViewType<Arg0Type>::LogicalValue(arr0_it()), - GetViewType<Arg1Type>::LogicalValue(arr1_it())); - }; - auto visit_null = [&]() { - arr0_it(); - arr1_it(); - null_func(); - }; - VisitTwoBitBlocksVoid(arr0.buffers[0], arr0.offset, arr1.buffers[0], arr1.offset, - arr0.length, std::move(visit_valid), std::move(visit_null)); -} - -// ---------------------------------------------------------------------- -// Reusable type resolvers - -Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs); - -// ---------------------------------------------------------------------- -// Generate an array kernel given template classes - +// Like VisitArrayValuesInline, but for binary functions. + +template <typename Arg0Type, typename Arg1Type, typename VisitFunc, typename NullFunc> +static void VisitTwoArrayValuesInline(const ArrayData& arr0, const ArrayData& arr1, + VisitFunc&& valid_func, NullFunc&& null_func) { + ArrayIterator<Arg0Type> arr0_it(arr0); + ArrayIterator<Arg1Type> arr1_it(arr1); + + auto visit_valid = [&](int64_t i) { + valid_func(GetViewType<Arg0Type>::LogicalValue(arr0_it()), + GetViewType<Arg1Type>::LogicalValue(arr1_it())); + }; + auto visit_null = [&]() { + arr0_it(); + arr1_it(); + null_func(); + }; + VisitTwoBitBlocksVoid(arr0.buffers[0], arr0.offset, arr1.buffers[0], arr1.offset, + arr0.length, std::move(visit_valid), std::move(visit_null)); +} + +// ---------------------------------------------------------------------- +// Reusable type resolvers + +Result<ValueDescr> FirstType(KernelContext*, const std::vector<ValueDescr>& descrs); + +// ---------------------------------------------------------------------- +// Generate an array kernel given template classes + Status ExecFail(KernelContext* ctx, const ExecBatch& batch, Datum* out); - -ArrayKernelExec MakeFlippedBinaryExec(ArrayKernelExec exec); - -// ---------------------------------------------------------------------- -// Helpers for iterating over common DataType instances for adding kernels to -// functions - -const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes(); -const std::vector<std::shared_ptr<DataType>>& StringTypes(); -const std::vector<std::shared_ptr<DataType>>& SignedIntTypes(); -const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes(); -const std::vector<std::shared_ptr<DataType>>& IntTypes(); -const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes(); + +ArrayKernelExec MakeFlippedBinaryExec(ArrayKernelExec exec); + +// ---------------------------------------------------------------------- +// Helpers for iterating over common DataType instances for adding kernels to +// functions + +const std::vector<std::shared_ptr<DataType>>& BaseBinaryTypes(); +const std::vector<std::shared_ptr<DataType>>& StringTypes(); +const std::vector<std::shared_ptr<DataType>>& SignedIntTypes(); +const std::vector<std::shared_ptr<DataType>>& UnsignedIntTypes(); +const std::vector<std::shared_ptr<DataType>>& IntTypes(); +const std::vector<std::shared_ptr<DataType>>& FloatingPointTypes(); const std::vector<Type::type>& DecimalTypeIds(); - -ARROW_EXPORT -const std::vector<TimeUnit::type>& AllTimeUnits(); - -// 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<std::shared_ptr<DataType>>& ExampleParametricTypes(); - -// Number types without boolean -const std::vector<std::shared_ptr<DataType>>& NumericTypes(); - -// Temporal types including time and timestamps for each unit -const std::vector<std::shared_ptr<DataType>>& TemporalTypes(); - -// Integer, floating point, base binary, and temporal -const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes(); - -// ---------------------------------------------------------------------- -// "Applicators" take an operator definition (which may be scalar-valued or -// array-valued) and creates an ArrayKernelExec which can be used to add an -// ArrayKernel to a Function. - -namespace applicator { - -// Generate an ArrayKernelExec given a functor that handles all of its own -// iteration, etc. -// -// Operator must implement -// + +ARROW_EXPORT +const std::vector<TimeUnit::type>& AllTimeUnits(); + +// 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<std::shared_ptr<DataType>>& ExampleParametricTypes(); + +// Number types without boolean +const std::vector<std::shared_ptr<DataType>>& NumericTypes(); + +// Temporal types including time and timestamps for each unit +const std::vector<std::shared_ptr<DataType>>& TemporalTypes(); + +// Integer, floating point, base binary, and temporal +const std::vector<std::shared_ptr<DataType>>& PrimitiveTypes(); + +// ---------------------------------------------------------------------- +// "Applicators" take an operator definition (which may be scalar-valued or +// array-valued) and creates an ArrayKernelExec which can be used to add an +// ArrayKernel 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 ArrayData& in, ArrayData* out) // static Status Call(KernelContext*, const Scalar& in, Scalar* out) -template <typename Operator> +template <typename Operator> static Status SimpleUnary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (batch[0].kind() == Datum::SCALAR) { + if (batch[0].kind() == Datum::SCALAR) { return Operator::Call(ctx, *batch[0].scalar(), out->scalar().get()); - } else if (batch.length > 0) { + } else if (batch.length > 0) { return Operator::Call(ctx, *batch[0].array(), out->mutable_array()); - } + } return Status::OK(); -} - -// Generate an ArrayKernelExec given a functor that handles all of its own -// iteration, etc. -// -// Operator must implement -// +} + +// Generate an ArrayKernelExec given a functor that handles all of its own +// iteration, etc. +// +// Operator must implement +// // static Status Call(KernelContext*, const ArrayData& arg0, const ArrayData& arg1, // ArrayData* out) // static Status Call(KernelContext*, const ArrayData& arg0, const Scalar& arg1, @@ -508,7 +508,7 @@ static Status SimpleUnary(KernelContext* ctx, const ExecBatch& batch, Datum* out // ArrayData* out) // static Status Call(KernelContext*, const Scalar& arg0, const Scalar& arg1, // Scalar* out) -template <typename Operator> +template <typename Operator> static Status SimpleBinary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { if (batch.length == 0) return Status::OK(); @@ -528,50 +528,50 @@ static Status SimpleBinary(KernelContext* ctx, const ExecBatch& batch, Datum* ou return Operator::Call(ctx, *batch[0].scalar(), *batch[1].scalar(), out->scalar().get()); } - } -} - -// 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 <typename Type, typename Enable = void> -struct OutputAdapter; - -template <typename Type> -struct OutputAdapter<Type, enable_if_boolean<Type>> { - template <typename Generator> + } +} + +// 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 <typename Type, typename Enable = void> +struct OutputAdapter; + +template <typename Type> +struct OutputAdapter<Type, enable_if_boolean<Type>> { + template <typename Generator> static Status Write(KernelContext*, Datum* out, Generator&& generator) { - ArrayData* out_arr = out->mutable_array(); - auto out_bitmap = out_arr->buffers[1]->mutable_data(); - GenerateBitsUnrolled(out_bitmap, out_arr->offset, out_arr->length, - std::forward<Generator>(generator)); + ArrayData* out_arr = out->mutable_array(); + auto out_bitmap = out_arr->buffers[1]->mutable_data(); + GenerateBitsUnrolled(out_bitmap, out_arr->offset, out_arr->length, + std::forward<Generator>(generator)); return Status::OK(); - } -}; - -template <typename Type> -struct OutputAdapter<Type, enable_if_has_c_type_not_boolean<Type>> { - template <typename Generator> + } +}; + +template <typename Type> +struct OutputAdapter<Type, enable_if_has_c_type_not_boolean<Type>> { + template <typename Generator> static Status Write(KernelContext*, Datum* out, Generator&& generator) { - ArrayData* out_arr = out->mutable_array(); - auto out_data = out_arr->GetMutableValues<typename Type::c_type>(1); - // TODO: Is this as fast as a more explicitly inlined function? - for (int64_t i = 0; i < out_arr->length; ++i) { - *out_data++ = generator(); - } + ArrayData* out_arr = out->mutable_array(); + auto out_data = out_arr->GetMutableValues<typename Type::c_type>(1); + // TODO: Is this as fast as a more explicitly inlined function? + for (int64_t i = 0; i < out_arr->length; ++i) { + *out_data++ = generator(); + } return Status::OK(); - } -}; - -template <typename Type> -struct OutputAdapter<Type, enable_if_base_binary<Type>> { - template <typename Generator> + } +}; + +template <typename Type> +struct OutputAdapter<Type, enable_if_base_binary<Type>> { + template <typename Generator> static Status Write(KernelContext* ctx, Datum* out, Generator&& generator) { return Status::NotImplemented("NYI"); - } -}; - + } +}; + template <typename Type> struct OutputAdapter<Type, enable_if_decimal<Type>> { using T = typename TypeTraits<Type>::ScalarType::ValueType; @@ -588,578 +588,578 @@ struct OutputAdapter<Type, enable_if_decimal<Type>> { } }; -// 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 <typename OutValue, typename Arg0Value> +// 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 <typename OutValue, typename Arg0Value> // static OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) { -// // implementation +// // implementation // // NOTE: "status" should only populated with errors, // // leave it unmodified to indicate Status::OK() -// } -// }; -template <typename OutType, typename Arg0Type, typename Op> -struct ScalarUnary { - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - +// } +// }; +template <typename OutType, typename Arg0Type, typename Op> +struct ScalarUnary { + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + static Status ExecArray(KernelContext* ctx, const ArrayData& arg0, Datum* out) { Status st = Status::OK(); - ArrayIterator<Arg0Type> arg0_it(arg0); + ArrayIterator<Arg0Type> arg0_it(arg0); RETURN_NOT_OK(OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { return Op::template Call<OutValue, Arg0Value>(ctx, arg0_it(), &st); })); return st; - } - + } + static Status ExecScalar(KernelContext* ctx, const Scalar& arg0, Datum* out) { Status st = Status::OK(); Scalar* out_scalar = out->scalar().get(); - if (arg0.is_valid) { - Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + if (arg0.is_valid) { + Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); out_scalar->is_valid = true; BoxScalar<OutType>::Box(Op::template Call<OutValue, Arg0Value>(ctx, arg0_val, &st), out_scalar); - } else { + } else { out_scalar->is_valid = false; - } + } return st; - } - + } + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (batch[0].kind() == Datum::ARRAY) { + if (batch[0].kind() == Datum::ARRAY) { return ExecArray(ctx, *batch[0].array(), out); - } else { + } else { return ExecScalar(ctx, *batch[0].scalar(), out); - } - } -}; - -// An alternative to ScalarUnary that Applies a scalar operation with state on -// only the not-null values of a single array -template <typename OutType, typename Arg0Type, typename Op> -struct ScalarUnaryNotNullStateful { - using ThisType = ScalarUnaryNotNullStateful<OutType, Arg0Type, Op>; - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - - Op op; - explicit ScalarUnaryNotNullStateful(Op op) : op(std::move(op)) {} - - // NOTE: In ArrayExec<Type>, Type is really OutputType - - template <typename Type, typename Enable = void> - struct ArrayExec { + } + } +}; + +// An alternative to ScalarUnary that Applies a scalar operation with state on +// only the not-null values of a single array +template <typename OutType, typename Arg0Type, typename Op> +struct ScalarUnaryNotNullStateful { + using ThisType = ScalarUnaryNotNullStateful<OutType, Arg0Type, Op>; + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + + Op op; + explicit ScalarUnaryNotNullStateful(Op op) : op(std::move(op)) {} + + // NOTE: In ArrayExec<Type>, Type is really OutputType + + template <typename Type, typename Enable = void> + struct ArrayExec { static Status Exec(const ThisType& functor, KernelContext* ctx, const ExecBatch& batch, Datum* out) { - ARROW_LOG(FATAL) << "Missing ArrayExec specialization for output type " - << out->type(); + ARROW_LOG(FATAL) << "Missing ArrayExec specialization for output type " + << out->type(); return Status::NotImplemented("NYI"); - } - }; - - template <typename Type> - struct ArrayExec< - Type, enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value>> { + } + }; + + template <typename Type> + struct ArrayExec< + Type, enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value>> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, Datum* out) { Status st = Status::OK(); - ArrayData* out_arr = out->mutable_array(); - auto out_data = out_arr->GetMutableValues<OutValue>(1); - VisitArrayValuesInline<Arg0Type>( - arg0, - [&](Arg0Value v) { + ArrayData* out_arr = out->mutable_array(); + auto out_data = out_arr->GetMutableValues<OutValue>(1); + VisitArrayValuesInline<Arg0Type>( + arg0, + [&](Arg0Value v) { *out_data++ = functor.op.template Call<OutValue, Arg0Value>(ctx, v, &st); - }, - [&]() { - // null + }, + [&]() { + // null *out_data++ = OutValue{}; - }); + }); return st; - } - }; - - template <typename Type> - struct ArrayExec<Type, enable_if_base_binary<Type>> { + } + }; + + template <typename Type> + struct ArrayExec<Type, enable_if_base_binary<Type>> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, Datum* 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<Type>::BuilderType builder; + // 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<Type>::BuilderType builder; Status st = Status::OK(); RETURN_NOT_OK(VisitArrayValuesInline<Arg0Type>( arg0, [&](Arg0Value v) { return builder.Append(functor.op.Call(ctx, v, &st)); }, [&]() { return builder.AppendNull(); })); if (st.ok()) { - std::shared_ptr<ArrayData> result; + std::shared_ptr<ArrayData> result; RETURN_NOT_OK(builder.FinishInternal(&result)); - out->value = std::move(result); - } + out->value = std::move(result); + } return st; - } - }; - - template <typename Type> - struct ArrayExec<Type, enable_if_t<is_boolean_type<Type>::value>> { + } + }; + + template <typename Type> + struct ArrayExec<Type, enable_if_t<is_boolean_type<Type>::value>> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, Datum* out) { Status st = Status::OK(); - ArrayData* out_arr = out->mutable_array(); - FirstTimeBitmapWriter out_writer(out_arr->buffers[1]->mutable_data(), - out_arr->offset, out_arr->length); - VisitArrayValuesInline<Arg0Type>( - arg0, - [&](Arg0Value v) { + ArrayData* out_arr = out->mutable_array(); + FirstTimeBitmapWriter out_writer(out_arr->buffers[1]->mutable_data(), + out_arr->offset, out_arr->length); + VisitArrayValuesInline<Arg0Type>( + arg0, + [&](Arg0Value v) { if (functor.op.template Call<OutValue, Arg0Value>(ctx, v, &st)) { - out_writer.Set(); - } - out_writer.Next(); - }, - [&]() { - // null - out_writer.Clear(); - out_writer.Next(); - }); - out_writer.Finish(); + out_writer.Set(); + } + out_writer.Next(); + }, + [&]() { + // null + out_writer.Clear(); + out_writer.Next(); + }); + out_writer.Finish(); return st; - } - }; - - template <typename Type> + } + }; + + template <typename Type> struct ArrayExec<Type, enable_if_decimal<Type>> { static Status Exec(const ThisType& functor, KernelContext* ctx, const ArrayData& arg0, Datum* out) { Status st = Status::OK(); - ArrayData* out_arr = out->mutable_array(); + ArrayData* out_arr = out->mutable_array(); // Decimal128 data buffers are not safely reinterpret_cast-able on big-endian using endian_agnostic = std::array<uint8_t, sizeof(typename TypeTraits<Type>::ScalarType::ValueType)>; auto out_data = out_arr->GetMutableValues<endian_agnostic>(1); - VisitArrayValuesInline<Arg0Type>( - arg0, - [&](Arg0Value v) { + VisitArrayValuesInline<Arg0Type>( + arg0, + [&](Arg0Value v) { functor.op.template Call<OutValue, Arg0Value>(ctx, v, &st) .ToBytes(out_data++->data()); - }, + }, [&]() { // null std::memset(out_data, 0, sizeof(*out_data)); ++out_data; }); return st; - } - }; - + } + }; + Status Scalar(KernelContext* ctx, const Scalar& arg0, Datum* out) { Status st = Status::OK(); - if (arg0.is_valid) { - Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + if (arg0.is_valid) { + Arg0Value arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); BoxScalar<OutType>::Box( this->op.template Call<OutValue, Arg0Value>(ctx, arg0_val, &st), out->scalar().get()); - } + } return st; - } - + } + Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (batch[0].kind() == Datum::ARRAY) { + if (batch[0].kind() == Datum::ARRAY) { return ArrayExec<OutType>::Exec(*this, ctx, *batch[0].array(), out); - } else { - return Scalar(ctx, *batch[0].scalar(), 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 <typename OutType, typename Arg0Type, typename Op> -struct ScalarUnaryNotNull { - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - + } else { + return Scalar(ctx, *batch[0].scalar(), 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 <typename OutType, typename Arg0Type, typename Op> +struct ScalarUnaryNotNull { + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - // Seed kernel with dummy state - ScalarUnaryNotNullStateful<OutType, Arg0Type, Op> 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 <typename OutValue, typename Arg0Value, typename Arg1Value> + // Seed kernel with dummy state + ScalarUnaryNotNullStateful<OutType, Arg0Type, Op> 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 <typename OutValue, typename Arg0Value, typename Arg1Value> // static OutValue Call(KernelContext* ctx, Arg0Value arg0, Arg1Value arg1, Status* st) // { -// // implementation +// // implementation // // NOTE: "status" should only populated with errors, // // leave it unmodified to indicate Status::OK() -// } -// }; -template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> -struct ScalarBinary { - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - using Arg1Value = typename GetViewType<Arg1Type>::T; - +// } +// }; +template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> +struct ScalarBinary { + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + using Arg1Value = typename GetViewType<Arg1Type>::T; + static Status ArrayArray(KernelContext* ctx, const ArrayData& arg0, const ArrayData& arg1, Datum* out) { Status st = Status::OK(); - ArrayIterator<Arg0Type> arg0_it(arg0); - ArrayIterator<Arg1Type> arg1_it(arg1); + ArrayIterator<Arg0Type> arg0_it(arg0); + ArrayIterator<Arg1Type> arg1_it(arg1); RETURN_NOT_OK(OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { return Op::template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_it(), arg1_it(), &st); })); return st; - } - + } + static Status ArrayScalar(KernelContext* ctx, const ArrayData& arg0, const Scalar& arg1, Datum* out) { Status st = Status::OK(); - ArrayIterator<Arg0Type> arg0_it(arg0); - auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); + ArrayIterator<Arg0Type> arg0_it(arg0); + auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); RETURN_NOT_OK(OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { return Op::template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_it(), arg1_val, &st); })); return st; - } - + } + static Status ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArrayData& arg1, Datum* out) { Status st = Status::OK(); - auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); - ArrayIterator<Arg1Type> arg1_it(arg1); + auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + ArrayIterator<Arg1Type> arg1_it(arg1); RETURN_NOT_OK(OutputAdapter<OutType>::Write(ctx, out, [&]() -> OutValue { return Op::template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, arg1_it(), &st); })); return st; - } - + } + static Status ScalarScalar(KernelContext* ctx, const Scalar& arg0, const Scalar& arg1, Datum* out) { Status st = Status::OK(); - if (out->scalar()->is_valid) { - auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); - auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); + if (out->scalar()->is_valid) { + auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); BoxScalar<OutType>::Box( Op::template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, arg1_val, &st), out->scalar().get()); - } + } return st; - } - + } + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (batch[0].kind() == Datum::ARRAY) { - if (batch[1].kind() == Datum::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].kind() == Datum::ARRAY) { - return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); - } else { - return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); - } - } - } -}; - -// An alternative to ScalarBinary that Applies a scalar operation with state on -// only the value pairs which are not-null in both arrays -template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> -struct ScalarBinaryNotNullStateful { - using ThisType = ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op>; - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - using Arg1Value = typename GetViewType<Arg1Type>::T; - - Op op; - explicit ScalarBinaryNotNullStateful(Op op) : op(std::move(op)) {} - - // NOTE: In ArrayExec<Type>, Type is really OutputType - + if (batch[0].kind() == Datum::ARRAY) { + if (batch[1].kind() == Datum::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].kind() == Datum::ARRAY) { + return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); + } else { + return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); + } + } + } +}; + +// An alternative to ScalarBinary that Applies a scalar operation with state on +// only the value pairs which are not-null in both arrays +template <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> +struct ScalarBinaryNotNullStateful { + using ThisType = ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op>; + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + using Arg1Value = typename GetViewType<Arg1Type>::T; + + Op op; + explicit ScalarBinaryNotNullStateful(Op op) : op(std::move(op)) {} + + // NOTE: In ArrayExec<Type>, Type is really OutputType + Status ArrayArray(KernelContext* ctx, const ArrayData& arg0, const ArrayData& arg1, Datum* out) { Status st = Status::OK(); - OutputArrayWriter<OutType> writer(out->mutable_array()); - VisitTwoArrayValuesInline<Arg0Type, Arg1Type>( - arg0, arg1, - [&](Arg0Value u, Arg1Value v) { + OutputArrayWriter<OutType> writer(out->mutable_array()); + VisitTwoArrayValuesInline<Arg0Type, Arg1Type>( + arg0, arg1, + [&](Arg0Value u, Arg1Value v) { writer.Write(op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, u, v, &st)); - }, - [&]() { writer.WriteNull(); }); + }, + [&]() { writer.WriteNull(); }); return st; - } - + } + Status ArrayScalar(KernelContext* ctx, const ArrayData& arg0, const Scalar& arg1, Datum* out) { Status st = Status::OK(); - OutputArrayWriter<OutType> writer(out->mutable_array()); - if (arg1.is_valid) { - const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); - VisitArrayValuesInline<Arg0Type>( - arg0, - [&](Arg0Value u) { - writer.Write( + OutputArrayWriter<OutType> writer(out->mutable_array()); + if (arg1.is_valid) { + const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); + VisitArrayValuesInline<Arg0Type>( + arg0, + [&](Arg0Value u) { + writer.Write( op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, u, arg1_val, &st)); - }, - [&]() { writer.WriteNull(); }); + }, + [&]() { writer.WriteNull(); }); } else { writer.WriteAllNull(out->mutable_array()->length); - } + } return st; - } - + } + Status ScalarArray(KernelContext* ctx, const Scalar& arg0, const ArrayData& arg1, Datum* out) { Status st = Status::OK(); - OutputArrayWriter<OutType> writer(out->mutable_array()); - if (arg0.is_valid) { - const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); - VisitArrayValuesInline<Arg1Type>( - arg1, - [&](Arg1Value v) { - writer.Write( + OutputArrayWriter<OutType> writer(out->mutable_array()); + if (arg0.is_valid) { + const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + VisitArrayValuesInline<Arg1Type>( + arg1, + [&](Arg1Value v) { + writer.Write( op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, v, &st)); - }, - [&]() { writer.WriteNull(); }); + }, + [&]() { writer.WriteNull(); }); } else { writer.WriteAllNull(out->mutable_array()->length); - } + } return st; - } - + } + Status ScalarScalar(KernelContext* ctx, const Scalar& arg0, const Scalar& arg1, Datum* out) { Status st = Status::OK(); - if (arg0.is_valid && arg1.is_valid) { - const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); - const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); - BoxScalar<OutType>::Box( + if (arg0.is_valid && arg1.is_valid) { + const auto arg0_val = UnboxScalar<Arg0Type>::Unbox(arg0); + const auto arg1_val = UnboxScalar<Arg1Type>::Unbox(arg1); + BoxScalar<OutType>::Box( op.template Call<OutValue, Arg0Value, Arg1Value>(ctx, arg0_val, arg1_val, &st), - out->scalar().get()); - } + out->scalar().get()); + } return st; - } - + } + Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (batch[0].kind() == Datum::ARRAY) { - if (batch[1].kind() == Datum::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].kind() == Datum::ARRAY) { - return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); - } else { - return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); - } - } - } -}; - -// 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 <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> -struct ScalarBinaryNotNull { - using OutValue = typename GetOutputType<OutType>::T; - using Arg0Value = typename GetViewType<Arg0Type>::T; - using Arg1Value = typename GetViewType<Arg1Type>::T; - + if (batch[0].kind() == Datum::ARRAY) { + if (batch[1].kind() == Datum::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].kind() == Datum::ARRAY) { + return ScalarArray(ctx, *batch[0].scalar(), *batch[1].array(), out); + } else { + return ScalarScalar(ctx, *batch[0].scalar(), *batch[1].scalar(), out); + } + } + } +}; + +// 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 <typename OutType, typename Arg0Type, typename Arg1Type, typename Op> +struct ScalarBinaryNotNull { + using OutValue = typename GetOutputType<OutType>::T; + using Arg0Value = typename GetViewType<Arg0Type>::T; + using Arg1Value = typename GetViewType<Arg1Type>::T; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - // Seed kernel with dummy state - ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op> kernel({}); - return kernel.Exec(ctx, batch, out); - } -}; - -// A kernel exec generator for binary kernels where both input types are the -// same -template <typename OutType, typename ArgType, typename Op> -using ScalarBinaryEqualTypes = ScalarBinary<OutType, ArgType, ArgType, Op>; - -// A kernel exec generator for non-null binary kernels where both input types are the -// same -template <typename OutType, typename ArgType, typename Op> -using ScalarBinaryNotNullEqualTypes = ScalarBinaryNotNull<OutType, ArgType, ArgType, Op>; - -} // 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 <typename Type0, typename Type1, Args...> -// struct FUNCTOR { -// static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* 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<FUNCTOR, Type0>(int32()) will select a kernel -// instantiated like FUNCTOR<Type0, Int32Type>. 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<DataType>& 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 - -// GD for numeric types (integer and floating point) -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateNumeric(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::INT8: - return Generator<Type0, Int8Type, Args...>::Exec; - case Type::UINT8: - return Generator<Type0, UInt8Type, Args...>::Exec; - case Type::INT16: - return Generator<Type0, Int16Type, Args...>::Exec; - case Type::UINT16: - return Generator<Type0, UInt16Type, Args...>::Exec; - case Type::INT32: - return Generator<Type0, Int32Type, Args...>::Exec; - case Type::UINT32: - return Generator<Type0, UInt32Type, Args...>::Exec; - case Type::INT64: - return Generator<Type0, Int64Type, Args...>::Exec; - case Type::UINT64: - return Generator<Type0, UInt64Type, Args...>::Exec; - case Type::FLOAT: - return Generator<Type0, FloatType, Args...>::Exec; - case Type::DOUBLE: - return Generator<Type0, DoubleType, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -// Generate a kernel given a templated functor for floating point types -// -// See "Numeric" above for description of the generator functor -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateFloatingPoint(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::FLOAT: - return Generator<Type0, FloatType, Args...>::Exec; - case Type::DOUBLE: - return Generator<Type0, DoubleType, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -// Generate a kernel given a templated functor for integer types -// -// See "Numeric" above for description of the generator functor -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateInteger(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::INT8: - return Generator<Type0, Int8Type, Args...>::Exec; - case Type::INT16: - return Generator<Type0, Int16Type, Args...>::Exec; - case Type::INT32: - return Generator<Type0, Int32Type, Args...>::Exec; - case Type::INT64: - return Generator<Type0, Int64Type, Args...>::Exec; - case Type::UINT8: - return Generator<Type0, UInt8Type, Args...>::Exec; - case Type::UINT16: - return Generator<Type0, UInt16Type, Args...>::Exec; - case Type::UINT32: - return Generator<Type0, UInt32Type, Args...>::Exec; - case Type::UINT64: - return Generator<Type0, UInt64Type, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GeneratePhysicalInteger(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::INT8: - return Generator<Type0, Int8Type, Args...>::Exec; - case Type::INT16: - return Generator<Type0, Int16Type, Args...>::Exec; - case Type::INT32: - case Type::DATE32: - case Type::TIME32: - return Generator<Type0, Int32Type, Args...>::Exec; - case Type::INT64: - case Type::DATE64: - case Type::TIMESTAMP: - case Type::TIME64: - case Type::DURATION: - return Generator<Type0, Int64Type, Args...>::Exec; - case Type::UINT8: - return Generator<Type0, UInt8Type, Args...>::Exec; - case Type::UINT16: - return Generator<Type0, UInt16Type, Args...>::Exec; - case Type::UINT32: - return Generator<Type0, UInt32Type, Args...>::Exec; - case Type::UINT64: - return Generator<Type0, UInt64Type, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - + // Seed kernel with dummy state + ScalarBinaryNotNullStateful<OutType, Arg0Type, Arg1Type, Op> kernel({}); + return kernel.Exec(ctx, batch, out); + } +}; + +// A kernel exec generator for binary kernels where both input types are the +// same +template <typename OutType, typename ArgType, typename Op> +using ScalarBinaryEqualTypes = ScalarBinary<OutType, ArgType, ArgType, Op>; + +// A kernel exec generator for non-null binary kernels where both input types are the +// same +template <typename OutType, typename ArgType, typename Op> +using ScalarBinaryNotNullEqualTypes = ScalarBinaryNotNull<OutType, ArgType, ArgType, Op>; + +} // 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 <typename Type0, typename Type1, Args...> +// struct FUNCTOR { +// static void Exec(KernelContext* ctx, const ExecBatch& batch, Datum* 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<FUNCTOR, Type0>(int32()) will select a kernel +// instantiated like FUNCTOR<Type0, Int32Type>. 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<DataType>& 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 + +// GD for numeric types (integer and floating point) +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateNumeric(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::INT8: + return Generator<Type0, Int8Type, Args...>::Exec; + case Type::UINT8: + return Generator<Type0, UInt8Type, Args...>::Exec; + case Type::INT16: + return Generator<Type0, Int16Type, Args...>::Exec; + case Type::UINT16: + return Generator<Type0, UInt16Type, Args...>::Exec; + case Type::INT32: + return Generator<Type0, Int32Type, Args...>::Exec; + case Type::UINT32: + return Generator<Type0, UInt32Type, Args...>::Exec; + case Type::INT64: + return Generator<Type0, Int64Type, Args...>::Exec; + case Type::UINT64: + return Generator<Type0, UInt64Type, Args...>::Exec; + case Type::FLOAT: + return Generator<Type0, FloatType, Args...>::Exec; + case Type::DOUBLE: + return Generator<Type0, DoubleType, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +// Generate a kernel given a templated functor for floating point types +// +// See "Numeric" above for description of the generator functor +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateFloatingPoint(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::FLOAT: + return Generator<Type0, FloatType, Args...>::Exec; + case Type::DOUBLE: + return Generator<Type0, DoubleType, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +// Generate a kernel given a templated functor for integer types +// +// See "Numeric" above for description of the generator functor +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateInteger(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::INT8: + return Generator<Type0, Int8Type, Args...>::Exec; + case Type::INT16: + return Generator<Type0, Int16Type, Args...>::Exec; + case Type::INT32: + return Generator<Type0, Int32Type, Args...>::Exec; + case Type::INT64: + return Generator<Type0, Int64Type, Args...>::Exec; + case Type::UINT8: + return Generator<Type0, UInt8Type, Args...>::Exec; + case Type::UINT16: + return Generator<Type0, UInt16Type, Args...>::Exec; + case Type::UINT32: + return Generator<Type0, UInt32Type, Args...>::Exec; + case Type::UINT64: + return Generator<Type0, UInt64Type, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GeneratePhysicalInteger(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::INT8: + return Generator<Type0, Int8Type, Args...>::Exec; + case Type::INT16: + return Generator<Type0, Int16Type, Args...>::Exec; + case Type::INT32: + case Type::DATE32: + case Type::TIME32: + return Generator<Type0, Int32Type, Args...>::Exec; + case Type::INT64: + case Type::DATE64: + case Type::TIMESTAMP: + case Type::TIME64: + case Type::DURATION: + return Generator<Type0, Int64Type, Args...>::Exec; + case Type::UINT8: + return Generator<Type0, UInt8Type, Args...>::Exec; + case Type::UINT16: + return Generator<Type0, UInt16Type, Args...>::Exec; + case Type::UINT32: + return Generator<Type0, UInt32Type, Args...>::Exec; + case Type::UINT64: + return Generator<Type0, UInt64Type, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + template <template <typename... Args> class Generator, typename... Args> ArrayKernelExec GeneratePhysicalNumeric(detail::GetTypeId get_id) { switch (get_id.id) { @@ -1195,68 +1195,68 @@ ArrayKernelExec GeneratePhysicalNumeric(detail::GetTypeId get_id) { } } -// Generate a kernel given a templated functor for integer types -// -// See "Numeric" above for description of the generator functor -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateSignedInteger(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::INT8: - return Generator<Type0, Int8Type, Args...>::Exec; - case Type::INT16: - return Generator<Type0, Int16Type, Args...>::Exec; - case Type::INT32: - return Generator<Type0, Int32Type, Args...>::Exec; - case Type::INT64: - return Generator<Type0, Int64Type, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -// Generate a kernel given a templated functor. Only a single template is -// instantiated for each bit width, and the functor is expected to treat types -// of the same bit width the same without utilizing any type-specific behavior -// (e.g. int64 should be handled equivalent to uint64 or double -- all 64 -// bits). -// -// See "Numeric" above for description of the generator functor +// Generate a kernel given a templated functor for integer types +// +// See "Numeric" above for description of the generator functor +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateSignedInteger(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::INT8: + return Generator<Type0, Int8Type, Args...>::Exec; + case Type::INT16: + return Generator<Type0, Int16Type, Args...>::Exec; + case Type::INT32: + return Generator<Type0, Int32Type, Args...>::Exec; + case Type::INT64: + return Generator<Type0, Int64Type, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +// Generate a kernel given a templated functor. Only a single template is +// instantiated for each bit width, and the functor is expected to treat types +// of the same bit width the same without utilizing any type-specific behavior +// (e.g. int64 should be handled equivalent to uint64 or double -- all 64 +// bits). +// +// See "Numeric" above for description of the generator functor template <template <typename...> class Generator, typename... Args> -ArrayKernelExec GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::NA: +ArrayKernelExec GenerateTypeAgnosticPrimitive(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::NA: return Generator<NullType, Args...>::Exec; - case Type::BOOL: + case Type::BOOL: return Generator<BooleanType, Args...>::Exec; - case Type::UINT8: - case Type::INT8: + case Type::UINT8: + case Type::INT8: return Generator<UInt8Type, Args...>::Exec; - case Type::UINT16: - case Type::INT16: + case Type::UINT16: + case Type::INT16: return Generator<UInt16Type, Args...>::Exec; - case Type::UINT32: - case Type::INT32: - case Type::FLOAT: - case Type::DATE32: - case Type::TIME32: + case Type::UINT32: + case Type::INT32: + case Type::FLOAT: + case Type::DATE32: + case Type::TIME32: case Type::INTERVAL_MONTHS: return Generator<UInt32Type, Args...>::Exec; - case Type::UINT64: - case Type::INT64: - case Type::DOUBLE: - case Type::DATE64: - case Type::TIMESTAMP: - case Type::TIME64: - case Type::DURATION: + case Type::UINT64: + case Type::INT64: + case Type::DOUBLE: + case Type::DATE64: + case Type::TIMESTAMP: + case Type::TIME64: + case Type::DURATION: case Type::INTERVAL_DAY_TIME: return Generator<UInt64Type, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - + default: + DCHECK(false); + return ExecFail; + } +} + // similar to GenerateTypeAgnosticPrimitive, but for variable types template <template <typename...> class Generator, typename... Args> ArrayKernelExec GenerateTypeAgnosticVarBinaryBase(detail::GetTypeId get_id) { @@ -1273,69 +1273,69 @@ ArrayKernelExec GenerateTypeAgnosticVarBinaryBase(detail::GetTypeId get_id) { } } -// Generate a kernel given a templated functor for base binary types. Generates -// a single kernel for binary/string and large binary / large string. If your -// kernel implementation needs access to the specific type at compile time, -// please use BaseBinarySpecific. -// -// See "Numeric" above for description of the generator functor -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateVarBinaryBase(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::BINARY: - case Type::STRING: - return Generator<Type0, BinaryType, Args...>::Exec; - case Type::LARGE_BINARY: - case Type::LARGE_STRING: - return Generator<Type0, LargeBinaryType, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -// See BaseBinary documentation -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateVarBinary(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::BINARY: - return Generator<Type0, BinaryType, Args...>::Exec; - case Type::STRING: - return Generator<Type0, StringType, Args...>::Exec; - case Type::LARGE_BINARY: - return Generator<Type0, LargeBinaryType, Args...>::Exec; - case Type::LARGE_STRING: - return Generator<Type0, LargeStringType, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - -// Generate a kernel given a templated functor for temporal types -// -// See "Numeric" above for description of the generator functor -template <template <typename...> class Generator, typename Type0, typename... Args> -ArrayKernelExec GenerateTemporal(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::DATE32: - return Generator<Type0, Date32Type, Args...>::Exec; - case Type::DATE64: - return Generator<Type0, Date64Type, Args...>::Exec; - case Type::DURATION: - return Generator<Type0, DurationType, Args...>::Exec; - case Type::TIME32: - return Generator<Type0, Time32Type, Args...>::Exec; - case Type::TIME64: - return Generator<Type0, Time64Type, Args...>::Exec; - case Type::TIMESTAMP: - return Generator<Type0, TimestampType, Args...>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - +// Generate a kernel given a templated functor for base binary types. Generates +// a single kernel for binary/string and large binary / large string. If your +// kernel implementation needs access to the specific type at compile time, +// please use BaseBinarySpecific. +// +// See "Numeric" above for description of the generator functor +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateVarBinaryBase(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::BINARY: + case Type::STRING: + return Generator<Type0, BinaryType, Args...>::Exec; + case Type::LARGE_BINARY: + case Type::LARGE_STRING: + return Generator<Type0, LargeBinaryType, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +// See BaseBinary documentation +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateVarBinary(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::BINARY: + return Generator<Type0, BinaryType, Args...>::Exec; + case Type::STRING: + return Generator<Type0, StringType, Args...>::Exec; + case Type::LARGE_BINARY: + return Generator<Type0, LargeBinaryType, Args...>::Exec; + case Type::LARGE_STRING: + return Generator<Type0, LargeStringType, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + +// Generate a kernel given a templated functor for temporal types +// +// See "Numeric" above for description of the generator functor +template <template <typename...> class Generator, typename Type0, typename... Args> +ArrayKernelExec GenerateTemporal(detail::GetTypeId get_id) { + switch (get_id.id) { + case Type::DATE32: + return Generator<Type0, Date32Type, Args...>::Exec; + case Type::DATE64: + return Generator<Type0, Date64Type, Args...>::Exec; + case Type::DURATION: + return Generator<Type0, DurationType, Args...>::Exec; + case Type::TIME32: + return Generator<Type0, Time32Type, Args...>::Exec; + case Type::TIME64: + return Generator<Type0, Time64Type, Args...>::Exec; + case Type::TIMESTAMP: + return Generator<Type0, TimestampType, Args...>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + // Generate a kernel given a templated functor for decimal types // // See "Numeric" above for description of the generator functor @@ -1352,9 +1352,9 @@ ArrayKernelExec GenerateDecimal(detail::GetTypeId get_id) { } } -// END of kernel generator-dispatchers -// ---------------------------------------------------------------------- - +// END of kernel generator-dispatchers +// ---------------------------------------------------------------------- + ARROW_EXPORT void EnsureDictionaryDecoded(std::vector<ValueDescr>* descrs); @@ -1376,6 +1376,6 @@ std::shared_ptr<DataType> CommonTimestamp(const std::vector<ValueDescr>& descrs) ARROW_EXPORT std::shared_ptr<DataType> CommonBinary(const std::vector<ValueDescr>& descrs); -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/common.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/common.h index 9ee2ec977a..21244320f3 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/common.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/common.h @@ -1,54 +1,54 @@ -// 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 - -// IWYU pragma: begin_exports - -#include <cstdint> -#include <memory> -#include <string> -#include <type_traits> -#include <utility> -#include <vector> - -#include "arrow/array/data.h" -#include "arrow/buffer.h" -#include "arrow/chunked_array.h" -#include "arrow/compute/exec.h" -#include "arrow/compute/function.h" -#include "arrow/compute/kernel.h" -#include "arrow/compute/kernels/codegen_internal.h" -#include "arrow/compute/registry.h" -#include "arrow/datum.h" -#include "arrow/memory_pool.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_traits.h" -#include "arrow/util/checked_cast.h" -#include "arrow/util/logging.h" -#include "arrow/util/macros.h" -#include "arrow/util/string_view.h" - -// IWYU pragma: end_exports - -namespace arrow { - -using internal::checked_cast; -using internal::checked_pointer_cast; - -} // namespace arrow +// 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 + +// IWYU pragma: begin_exports + +#include <cstdint> +#include <memory> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include "arrow/array/data.h" +#include "arrow/buffer.h" +#include "arrow/chunked_array.h" +#include "arrow/compute/exec.h" +#include "arrow/compute/function.h" +#include "arrow/compute/kernel.h" +#include "arrow/compute/kernels/codegen_internal.h" +#include "arrow/compute/registry.h" +#include "arrow/datum.h" +#include "arrow/memory_pool.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_traits.h" +#include "arrow/util/checked_cast.h" +#include "arrow/util/logging.h" +#include "arrow/util/macros.h" +#include "arrow/util/string_view.h" + +// IWYU pragma: end_exports + +namespace arrow { + +using internal::checked_cast; +using internal::checked_pointer_cast; + +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc index f05cc0f3d3..a5d4a55774 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc @@ -1,83 +1,83 @@ -// 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. - +// 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. + #include <algorithm> #include <cmath> #include <limits> #include <utility> #include "arrow/compute/kernels/codegen_internal.h" -#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/common.h" #include "arrow/compute/kernels/util_internal.h" #include "arrow/type.h" #include "arrow/type_traits.h" #include "arrow/util/decimal.h" -#include "arrow/util/int_util_internal.h" -#include "arrow/util/macros.h" - -namespace arrow { - -using internal::AddWithOverflow; -using internal::DivideWithOverflow; -using internal::MultiplyWithOverflow; +#include "arrow/util/int_util_internal.h" +#include "arrow/util/macros.h" + +namespace arrow { + +using internal::AddWithOverflow; +using internal::DivideWithOverflow; +using internal::MultiplyWithOverflow; using internal::NegateWithOverflow; -using internal::SubtractWithOverflow; - -namespace compute { -namespace internal { - -using applicator::ScalarBinaryEqualTypes; -using applicator::ScalarBinaryNotNullEqualTypes; +using internal::SubtractWithOverflow; + +namespace compute { +namespace internal { + +using applicator::ScalarBinaryEqualTypes; +using applicator::ScalarBinaryNotNullEqualTypes; using applicator::ScalarUnary; using applicator::ScalarUnaryNotNull; - -namespace { - -template <typename T> -using is_unsigned_integer = std::integral_constant<bool, std::is_integral<T>::value && - std::is_unsigned<T>::value>; - -template <typename T> -using is_signed_integer = - std::integral_constant<bool, std::is_integral<T>::value && std::is_signed<T>::value>; - + +namespace { + +template <typename T> +using is_unsigned_integer = std::integral_constant<bool, std::is_integral<T>::value && + std::is_unsigned<T>::value>; + +template <typename T> +using is_signed_integer = + std::integral_constant<bool, std::is_integral<T>::value && std::is_signed<T>::value>; + template <typename T, typename R = T> using enable_if_signed_integer = enable_if_t<is_signed_integer<T>::value, R>; - + template <typename T, typename R = T> using enable_if_unsigned_integer = enable_if_t<is_unsigned_integer<T>::value, R>; - + template <typename T, typename R = T> -using enable_if_integer = +using enable_if_integer = enable_if_t<is_signed_integer<T>::value || is_unsigned_integer<T>::value, R>; - + template <typename T, typename R = T> using enable_if_floating_point = enable_if_t<std::is_floating_point<T>::value, R>; -template <typename T> +template <typename T> using enable_if_decimal = enable_if_t<std::is_same<Decimal128, T>::value || std::is_same<Decimal256, T>::value, T>; - -template <typename T, typename Unsigned = typename std::make_unsigned<T>::type> -constexpr Unsigned to_unsigned(T signed_) { - return static_cast<Unsigned>(signed_); -} - + +template <typename T, typename Unsigned = typename std::make_unsigned<T>::type> +constexpr Unsigned to_unsigned(T signed_) { + return static_cast<Unsigned>(signed_); +} + struct AbsoluteValue { template <typename T, typename Arg> static constexpr enable_if_floating_point<T> Call(KernelContext*, T arg, Status*) { @@ -119,201 +119,201 @@ struct AbsoluteValueChecked { } }; -struct Add { +struct Add { template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - return left + right; - } - + return left + right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_unsigned_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - return left + right; - } - + return left + right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_signed_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - return arrow::internal::SafeSignedAdd(left, right); - } + return arrow::internal::SafeSignedAdd(left, right); + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left + right; } -}; - -struct AddChecked { - template <typename T, typename Arg0, typename Arg1> +}; + +struct AddChecked { + template <typename T, typename Arg0, typename Arg1> static enable_if_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - T result = 0; - if (ARROW_PREDICT_FALSE(AddWithOverflow(left, right, &result))) { + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + T result = 0; + if (ARROW_PREDICT_FALSE(AddWithOverflow(left, right, &result))) { *st = Status::Invalid("overflow"); - } - return result; - } - - template <typename T, typename Arg0, typename Arg1> + } + return result; + } + + template <typename T, typename Arg0, typename Arg1> static enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return left + right; - } + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + return left + right; + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left + right; } -}; - -struct Subtract { +}; + +struct Subtract { template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return left - right; - } - + return left - right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_unsigned_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return left - right; - } - + return left - right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_signed_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return arrow::internal::SafeSignedSubtract(left, right); - } + return arrow::internal::SafeSignedSubtract(left, right); + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left + (-right); } -}; - -struct SubtractChecked { - template <typename T, typename Arg0, typename Arg1> +}; + +struct SubtractChecked { + template <typename T, typename Arg0, typename Arg1> static enable_if_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - T result = 0; - if (ARROW_PREDICT_FALSE(SubtractWithOverflow(left, right, &result))) { + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + T result = 0; + if (ARROW_PREDICT_FALSE(SubtractWithOverflow(left, right, &result))) { *st = Status::Invalid("overflow"); - } - return result; - } - - template <typename T, typename Arg0, typename Arg1> + } + return result; + } + + template <typename T, typename Arg0, typename Arg1> static enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return left - right; - } + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + return left - right; + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left + (-right); } -}; - -struct Multiply { - static_assert(std::is_same<decltype(int8_t() * int8_t()), int32_t>::value, ""); - static_assert(std::is_same<decltype(uint8_t() * uint8_t()), int32_t>::value, ""); - static_assert(std::is_same<decltype(int16_t() * int16_t()), int32_t>::value, ""); - static_assert(std::is_same<decltype(uint16_t() * uint16_t()), int32_t>::value, ""); - static_assert(std::is_same<decltype(int32_t() * int32_t()), int32_t>::value, ""); - static_assert(std::is_same<decltype(uint32_t() * uint32_t()), uint32_t>::value, ""); - static_assert(std::is_same<decltype(int64_t() * int64_t()), int64_t>::value, ""); - static_assert(std::is_same<decltype(uint64_t() * uint64_t()), uint64_t>::value, ""); - +}; + +struct Multiply { + static_assert(std::is_same<decltype(int8_t() * int8_t()), int32_t>::value, ""); + static_assert(std::is_same<decltype(uint8_t() * uint8_t()), int32_t>::value, ""); + static_assert(std::is_same<decltype(int16_t() * int16_t()), int32_t>::value, ""); + static_assert(std::is_same<decltype(uint16_t() * uint16_t()), int32_t>::value, ""); + static_assert(std::is_same<decltype(int32_t() * int32_t()), int32_t>::value, ""); + static_assert(std::is_same<decltype(uint32_t() * uint32_t()), uint32_t>::value, ""); + static_assert(std::is_same<decltype(int64_t() * int64_t()), int64_t>::value, ""); + static_assert(std::is_same<decltype(uint64_t() * uint64_t()), uint64_t>::value, ""); + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_floating_point<T> Call(KernelContext*, T left, T right, Status*) { - return left * right; - } - + return left * right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_t< is_unsigned_integer<T>::value && !std::is_same<T, uint16_t>::value, T> Call(KernelContext*, T left, T right, Status*) { - return left * right; - } - + return left * right; + } + template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_t< is_signed_integer<T>::value && !std::is_same<T, int16_t>::value, T> Call(KernelContext*, T left, T right, Status*) { - return to_unsigned(left) * to_unsigned(right); - } - - // Multiplication of 16 bit integer types implicitly promotes to signed 32 bit - // integer. However, some inputs may nevertheless overflow (which triggers undefined - // behaviour). Therefore we first cast to 32 bit unsigned integers where overflow is - // well defined. + return to_unsigned(left) * to_unsigned(right); + } + + // Multiplication of 16 bit integer types implicitly promotes to signed 32 bit + // integer. However, some inputs may nevertheless overflow (which triggers undefined + // behaviour). Therefore we first cast to 32 bit unsigned integers where overflow is + // well defined. template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_same<T, int16_t, T> Call(KernelContext*, int16_t left, int16_t right, Status*) { - return static_cast<uint32_t>(left) * static_cast<uint32_t>(right); - } + return static_cast<uint32_t>(left) * static_cast<uint32_t>(right); + } template <typename T, typename Arg0, typename Arg1> static constexpr enable_if_same<T, uint16_t, T> Call(KernelContext*, uint16_t left, uint16_t right, Status*) { - return static_cast<uint32_t>(left) * static_cast<uint32_t>(right); - } + return static_cast<uint32_t>(left) * static_cast<uint32_t>(right); + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left * right; } -}; - -struct MultiplyChecked { - template <typename T, typename Arg0, typename Arg1> +}; + +struct MultiplyChecked { + template <typename T, typename Arg0, typename Arg1> static enable_if_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - T result = 0; - if (ARROW_PREDICT_FALSE(MultiplyWithOverflow(left, right, &result))) { + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + T result = 0; + if (ARROW_PREDICT_FALSE(MultiplyWithOverflow(left, right, &result))) { *st = Status::Invalid("overflow"); - } - return result; - } - - template <typename T, typename Arg0, typename Arg1> + } + return result; + } + + template <typename T, typename Arg0, typename Arg1> static enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - return left * right; - } + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + return left * right; + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { return left * right; } -}; - -struct Divide { - template <typename T, typename Arg0, typename Arg1> +}; + +struct Divide { + template <typename T, typename Arg0, typename Arg1> static enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status*) { - return left / right; - } - - template <typename T, typename Arg0, typename Arg1> + return left / right; + } + + template <typename T, typename Arg0, typename Arg1> static enable_if_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - T result; - if (ARROW_PREDICT_FALSE(DivideWithOverflow(left, right, &result))) { - if (right == 0) { + T result; + if (ARROW_PREDICT_FALSE(DivideWithOverflow(left, right, &result))) { + if (right == 0) { *st = Status::Invalid("divide by zero"); - } else { - result = 0; - } - } - return result; - } + } else { + result = 0; + } + } + return result; + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { @@ -324,41 +324,41 @@ struct Divide { return left / right; } } -}; - -struct DivideChecked { - template <typename T, typename Arg0, typename Arg1> +}; + +struct DivideChecked { + template <typename T, typename Arg0, typename Arg1> static enable_if_integer<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - T result; - if (ARROW_PREDICT_FALSE(DivideWithOverflow(left, right, &result))) { - if (right == 0) { + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + T result; + if (ARROW_PREDICT_FALSE(DivideWithOverflow(left, right, &result))) { + if (right == 0) { *st = Status::Invalid("divide by zero"); - } else { + } else { *st = Status::Invalid("overflow"); - } - } - return result; - } - - template <typename T, typename Arg0, typename Arg1> + } + } + return result; + } + + template <typename T, typename Arg0, typename Arg1> static enable_if_floating_point<T> Call(KernelContext*, Arg0 left, Arg1 right, Status* st) { - static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); - if (ARROW_PREDICT_FALSE(right == 0)) { + static_assert(std::is_same<T, Arg0>::value && std::is_same<T, Arg1>::value, ""); + if (ARROW_PREDICT_FALSE(right == 0)) { *st = Status::Invalid("divide by zero"); - return 0; - } - return left / right; - } + return 0; + } + return left / right; + } template <typename T, typename Arg0, typename Arg1> static enable_if_decimal<T> Call(KernelContext* ctx, Arg0 left, Arg1 right, Status* st) { return Divide::Call<T>(ctx, left, right, st); } -}; - +}; + struct Negate { template <typename T, typename Arg> static constexpr enable_if_floating_point<T> Call(KernelContext*, Arg arg, Status*) { @@ -838,37 +838,37 @@ struct Trunc { } }; -// Generate a kernel given an arithmetic functor -template <template <typename... Args> class KernelGenerator, typename Op> +// Generate a kernel given an arithmetic functor +template <template <typename... Args> class KernelGenerator, typename Op> ArrayKernelExec ArithmeticExecFromOp(detail::GetTypeId get_id) { - switch (get_id.id) { - case Type::INT8: - return KernelGenerator<Int8Type, Int8Type, Op>::Exec; - case Type::UINT8: - return KernelGenerator<UInt8Type, UInt8Type, Op>::Exec; - case Type::INT16: - return KernelGenerator<Int16Type, Int16Type, Op>::Exec; - case Type::UINT16: - return KernelGenerator<UInt16Type, UInt16Type, Op>::Exec; - case Type::INT32: - return KernelGenerator<Int32Type, Int32Type, Op>::Exec; - case Type::UINT32: - return KernelGenerator<UInt32Type, UInt32Type, Op>::Exec; - case Type::INT64: - case Type::TIMESTAMP: - return KernelGenerator<Int64Type, Int64Type, Op>::Exec; - case Type::UINT64: - return KernelGenerator<UInt64Type, UInt64Type, Op>::Exec; - case Type::FLOAT: - return KernelGenerator<FloatType, FloatType, Op>::Exec; - case Type::DOUBLE: - return KernelGenerator<DoubleType, DoubleType, Op>::Exec; - default: - DCHECK(false); - return ExecFail; - } -} - + switch (get_id.id) { + case Type::INT8: + return KernelGenerator<Int8Type, Int8Type, Op>::Exec; + case Type::UINT8: + return KernelGenerator<UInt8Type, UInt8Type, Op>::Exec; + case Type::INT16: + return KernelGenerator<Int16Type, Int16Type, Op>::Exec; + case Type::UINT16: + return KernelGenerator<UInt16Type, UInt16Type, Op>::Exec; + case Type::INT32: + return KernelGenerator<Int32Type, Int32Type, Op>::Exec; + case Type::UINT32: + return KernelGenerator<UInt32Type, UInt32Type, Op>::Exec; + case Type::INT64: + case Type::TIMESTAMP: + return KernelGenerator<Int64Type, Int64Type, Op>::Exec; + case Type::UINT64: + return KernelGenerator<UInt64Type, UInt64Type, Op>::Exec; + case Type::FLOAT: + return KernelGenerator<FloatType, FloatType, Op>::Exec; + case Type::DOUBLE: + return KernelGenerator<DoubleType, DoubleType, Op>::Exec; + default: + DCHECK(false); + return ExecFail; + } +} + // Generate a kernel given a bitwise arithmetic functor. Assumes the // functor treats all integer types of equal width identically template <template <typename... Args> class KernelGenerator, typename Op> @@ -1050,7 +1050,7 @@ Result<ValueDescr> ResolveDecimalDivisionOutput(KernelContext*, }); } -template <typename Op> +template <typename Op> void AddDecimalBinaryKernels(const std::string& name, std::shared_ptr<ScalarFunction>* func) { OutputType out_type(null()); @@ -1182,26 +1182,26 @@ template <typename Op> std::shared_ptr<ScalarFunction> MakeArithmeticFunction(std::string name, const FunctionDoc* doc) { auto func = std::make_shared<ArithmeticFunction>(name, Arity::Binary(), doc); - for (const auto& ty : NumericTypes()) { + for (const auto& ty : NumericTypes()) { auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, Op>(ty); - DCHECK_OK(func->AddKernel({ty, ty}, ty, exec)); - } - return func; -} - -// Like MakeArithmeticFunction, but for arithmetic ops that need to run -// only on non-null output. -template <typename Op> + DCHECK_OK(func->AddKernel({ty, ty}, ty, exec)); + } + return func; +} + +// Like MakeArithmeticFunction, but for arithmetic ops that need to run +// only on non-null output. +template <typename Op> std::shared_ptr<ScalarFunction> MakeArithmeticFunctionNotNull(std::string name, const FunctionDoc* doc) { auto func = std::make_shared<ArithmeticFunction>(name, Arity::Binary(), doc); - for (const auto& ty : NumericTypes()) { + for (const auto& ty : NumericTypes()) { auto exec = ArithmeticExecFromOp<ScalarBinaryNotNullEqualTypes, Op>(ty); - DCHECK_OK(func->AddKernel({ty, ty}, ty, exec)); - } - return func; -} - + DCHECK_OK(func->AddKernel({ty, ty}, ty, exec)); + } + return func; +} + template <typename Op> std::shared_ptr<ScalarFunction> MakeUnaryArithmeticFunction(std::string name, const FunctionDoc* doc) { @@ -1606,10 +1606,10 @@ const FunctionDoc trunc_doc{ ("Calculate the nearest integer not greater in magnitude than to the " "argument element-wise."), {"x"}}; -} // namespace - -void RegisterScalarArithmetic(FunctionRegistry* registry) { - // ---------------------------------------------------------------------- +} // namespace + +void RegisterScalarArithmetic(FunctionRegistry* registry) { + // ---------------------------------------------------------------------- auto absolute_value = MakeUnaryArithmeticFunction<AbsoluteValue>("abs", &absolute_value_doc); DCHECK_OK(registry->AddFunction(std::move(absolute_value))); @@ -1622,54 +1622,54 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { // ---------------------------------------------------------------------- auto add = MakeArithmeticFunction<Add>("add", &add_doc); AddDecimalBinaryKernels<Add>("add", &add); - DCHECK_OK(registry->AddFunction(std::move(add))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(add))); + + // ---------------------------------------------------------------------- auto add_checked = MakeArithmeticFunctionNotNull<AddChecked>("add_checked", &add_checked_doc); AddDecimalBinaryKernels<AddChecked>("add_checked", &add_checked); - DCHECK_OK(registry->AddFunction(std::move(add_checked))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(add_checked))); + + // ---------------------------------------------------------------------- auto subtract = MakeArithmeticFunction<Subtract>("subtract", &sub_doc); AddDecimalBinaryKernels<Subtract>("subtract", &subtract); - - // Add subtract(timestamp, timestamp) -> duration - for (auto unit : AllTimeUnits()) { - InputType in_type(match::TimestampTypeUnit(unit)); + + // Add subtract(timestamp, timestamp) -> duration + for (auto unit : AllTimeUnits()) { + InputType in_type(match::TimestampTypeUnit(unit)); auto exec = ArithmeticExecFromOp<ScalarBinaryEqualTypes, Subtract>(Type::TIMESTAMP); - DCHECK_OK(subtract->AddKernel({in_type, in_type}, duration(unit), std::move(exec))); - } - - DCHECK_OK(registry->AddFunction(std::move(subtract))); - - // ---------------------------------------------------------------------- + DCHECK_OK(subtract->AddKernel({in_type, in_type}, duration(unit), std::move(exec))); + } + + DCHECK_OK(registry->AddFunction(std::move(subtract))); + + // ---------------------------------------------------------------------- auto subtract_checked = MakeArithmeticFunctionNotNull<SubtractChecked>( "subtract_checked", &sub_checked_doc); AddDecimalBinaryKernels<SubtractChecked>("subtract_checked", &subtract_checked); - DCHECK_OK(registry->AddFunction(std::move(subtract_checked))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(subtract_checked))); + + // ---------------------------------------------------------------------- auto multiply = MakeArithmeticFunction<Multiply>("multiply", &mul_doc); AddDecimalBinaryKernels<Multiply>("multiply", &multiply); - DCHECK_OK(registry->AddFunction(std::move(multiply))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(multiply))); + + // ---------------------------------------------------------------------- auto multiply_checked = MakeArithmeticFunctionNotNull<MultiplyChecked>( "multiply_checked", &mul_checked_doc); AddDecimalBinaryKernels<MultiplyChecked>("multiply_checked", &multiply_checked); - DCHECK_OK(registry->AddFunction(std::move(multiply_checked))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(multiply_checked))); + + // ---------------------------------------------------------------------- auto divide = MakeArithmeticFunctionNotNull<Divide>("divide", &div_doc); AddDecimalBinaryKernels<Divide>("divide", ÷); - DCHECK_OK(registry->AddFunction(std::move(divide))); - - // ---------------------------------------------------------------------- + DCHECK_OK(registry->AddFunction(std::move(divide))); + + // ---------------------------------------------------------------------- auto divide_checked = MakeArithmeticFunctionNotNull<DivideChecked>("divide_checked", &div_checked_doc); AddDecimalBinaryKernels<DivideChecked>("divide_checked", ÷_checked); - DCHECK_OK(registry->AddFunction(std::move(divide_checked))); + DCHECK_OK(registry->AddFunction(std::move(divide_checked))); // ---------------------------------------------------------------------- auto negate = MakeUnaryArithmeticFunction<Negate>("negate", &negate_doc); @@ -1816,8 +1816,8 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) { auto trunc = MakeUnaryArithmeticFunctionFloatingPoint<Trunc>("trunc", &trunc_doc); DCHECK_OK(registry->AddFunction(std::move(trunc))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_boolean.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_boolean.cc index 63fddcd1fe..7a0e3654ed 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_boolean.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_boolean.cc @@ -1,61 +1,61 @@ -// 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. - -#include <array> - -#include "arrow/compute/kernels/common.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap.h" -#include "arrow/util/bitmap_ops.h" - -namespace arrow { - -using internal::Bitmap; - -namespace compute { - -namespace { - -template <typename ComputeWord> -void ComputeKleene(ComputeWord&& compute_word, KernelContext* ctx, const ArrayData& left, - const ArrayData& right, ArrayData* out) { +// 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. + +#include <array> + +#include "arrow/compute/kernels/common.h" +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap.h" +#include "arrow/util/bitmap_ops.h" + +namespace arrow { + +using internal::Bitmap; + +namespace compute { + +namespace { + +template <typename ComputeWord> +void ComputeKleene(ComputeWord&& compute_word, KernelContext* ctx, const ArrayData& left, + const ArrayData& right, ArrayData* out) { DCHECK(left.null_count != 0 || right.null_count != 0) << "ComputeKleene is unnecessarily expensive for the non-null case"; - + Bitmap left_valid_bm{left.buffers[0], left.offset, left.length}; Bitmap left_data_bm{left.buffers[1], left.offset, left.length}; - + Bitmap right_valid_bm{right.buffers[0], right.offset, right.length}; Bitmap right_data_bm{right.buffers[1], right.offset, right.length}; - + std::array<Bitmap, 2> out_bms{Bitmap(out->buffers[0], out->offset, out->length), Bitmap(out->buffers[1], out->offset, out->length)}; - - auto apply = [&](uint64_t left_valid, uint64_t left_data, uint64_t right_valid, + + auto apply = [&](uint64_t left_valid, uint64_t left_data, uint64_t right_valid, uint64_t right_data, uint64_t* out_validity, uint64_t* out_data) { - auto left_true = left_valid & left_data; - auto left_false = left_valid & ~left_data; - - auto right_true = right_valid & right_data; - auto right_false = right_valid & ~right_data; - + auto left_true = left_valid & left_data; + auto left_false = left_valid & ~left_data; + + auto right_true = right_valid & right_data; + auto right_false = right_valid & ~right_data; + compute_word(left_true, left_false, right_true, right_false, out_validity, out_data); - }; - + }; + if (right.null_count == 0) { std::array<Bitmap, 3> in_bms{left_valid_bm, left_data_bm, right_data_bm}; Bitmap::VisitWordsAndWrite( @@ -65,7 +65,7 @@ void ComputeKleene(ComputeWord&& compute_word, KernelContext* ctx, const ArrayDa }); return; } - + if (left.null_count == 0) { std::array<Bitmap, 3> in_bms{left_data_bm, right_valid_bm, right_data_bm}; Bitmap::VisitWordsAndWrite( @@ -74,7 +74,7 @@ void ComputeKleene(ComputeWord&& compute_word, KernelContext* ctx, const ArrayDa apply(~uint64_t(0), in[0], in[1], in[2], &(out->at(0)), &(out->at(1))); }); return; - } + } DCHECK(left.null_count != 0 && right.null_count != 0); std::array<Bitmap, 4> in_bms{left_valid_bm, left_data_bm, right_valid_bm, @@ -84,8 +84,8 @@ void ComputeKleene(ComputeWord&& compute_word, KernelContext* ctx, const ArrayDa [&](const std::array<uint64_t, 4>& in, std::array<uint64_t, 2>* out) { apply(in[0], in[1], in[2], in[3], &(out->at(0)), &(out->at(1))); }); -} - +} + inline BooleanScalar InvertScalar(const Scalar& in) { return in.is_valid ? BooleanScalar(!checked_cast<const BooleanScalar&>(in).value) : BooleanScalar(); @@ -121,13 +121,13 @@ struct AndOp : Commutative<AndOp> { static Status Call(KernelContext* ctx, const Scalar& left, const Scalar& right, Scalar* out) { if (left.is_valid && right.is_valid) { - checked_cast<BooleanScalar*>(out)->value = + checked_cast<BooleanScalar*>(out)->value = checked_cast<const BooleanScalar&>(left).value && checked_cast<const BooleanScalar&>(right).value; - } + } return Status::OK(); - } - + } + static Status Call(KernelContext* ctx, const ArrayData& left, const Scalar& right, ArrayData* out) { if (right.is_valid) { @@ -136,17 +136,17 @@ struct AndOp : Commutative<AndOp> { : GetBitmap(*out, 1).SetBitsTo(false); } return Status::OK(); - } - + } + static Status Call(KernelContext* ctx, const ArrayData& left, const ArrayData& right, ArrayData* out) { - ::arrow::internal::BitmapAnd(left.buffers[1]->data(), left.offset, - right.buffers[1]->data(), right.offset, right.length, - out->offset, out->buffers[1]->mutable_data()); + ::arrow::internal::BitmapAnd(left.buffers[1]->data(), left.offset, + right.buffers[1]->data(), right.offset, right.length, + out->offset, out->buffers[1]->mutable_data()); return Status::OK(); - } -}; - + } +}; + struct KleeneAndOp : Commutative<KleeneAndOp> { using Commutative<KleeneAndOp>::Call; @@ -202,23 +202,23 @@ struct KleeneAndOp : Commutative<KleeneAndOp> { static Status Call(KernelContext* ctx, const ArrayData& left, const ArrayData& right, ArrayData* out) { - if (left.GetNullCount() == 0 && right.GetNullCount() == 0) { + if (left.GetNullCount() == 0 && right.GetNullCount() == 0) { out->null_count = 0; // Kleene kernels have validity bitmap pre-allocated. Therefore, set it to 1 BitUtil::SetBitmap(out->buffers[0]->mutable_data(), out->offset, out->length); return AndOp::Call(ctx, left, right, out); - } - auto compute_word = [](uint64_t left_true, uint64_t left_false, uint64_t right_true, - uint64_t right_false, uint64_t* out_valid, - uint64_t* out_data) { - *out_data = left_true & right_true; - *out_valid = left_false | right_false | (left_true & right_true); - }; - ComputeKleene(compute_word, ctx, left, right, out); + } + auto compute_word = [](uint64_t left_true, uint64_t left_false, uint64_t right_true, + uint64_t right_false, uint64_t* out_valid, + uint64_t* out_data) { + *out_data = left_true & right_true; + *out_valid = left_false | right_false | (left_true & right_true); + }; + ComputeKleene(compute_word, ctx, left, right, out); return Status::OK(); - } -}; - + } +}; + struct OrOp : Commutative<OrOp> { using Commutative<OrOp>::Call; @@ -244,13 +244,13 @@ struct OrOp : Commutative<OrOp> { static Status Call(KernelContext* ctx, const ArrayData& left, const ArrayData& right, ArrayData* out) { - ::arrow::internal::BitmapOr(left.buffers[1]->data(), left.offset, - right.buffers[1]->data(), right.offset, right.length, - out->offset, out->buffers[1]->mutable_data()); + ::arrow::internal::BitmapOr(left.buffers[1]->data(), left.offset, + right.buffers[1]->data(), right.offset, right.length, + out->offset, out->buffers[1]->mutable_data()); return Status::OK(); - } -}; - + } +}; + struct KleeneOrOp : Commutative<KleeneOrOp> { using Commutative<KleeneOrOp>::Call; @@ -306,25 +306,25 @@ struct KleeneOrOp : Commutative<KleeneOrOp> { static Status Call(KernelContext* ctx, const ArrayData& left, const ArrayData& right, ArrayData* out) { - if (left.GetNullCount() == 0 && right.GetNullCount() == 0) { + if (left.GetNullCount() == 0 && right.GetNullCount() == 0) { out->null_count = 0; // Kleene kernels have validity bitmap pre-allocated. Therefore, set it to 1 BitUtil::SetBitmap(out->buffers[0]->mutable_data(), out->offset, out->length); return OrOp::Call(ctx, left, right, out); - } - - static auto compute_word = [](uint64_t left_true, uint64_t left_false, - uint64_t right_true, uint64_t right_false, - uint64_t* out_valid, uint64_t* out_data) { - *out_data = left_true | right_true; - *out_valid = left_true | right_true | (left_false & right_false); - }; - + } + + static auto compute_word = [](uint64_t left_true, uint64_t left_false, + uint64_t right_true, uint64_t right_false, + uint64_t* out_valid, uint64_t* out_data) { + *out_data = left_true | right_true; + *out_valid = left_true | right_true | (left_false & right_false); + }; + ComputeKleene(compute_word, ctx, left, right, out); return Status::OK(); - } -}; - + } +}; + struct XorOp : Commutative<XorOp> { using Commutative<XorOp>::Call; @@ -350,13 +350,13 @@ struct XorOp : Commutative<XorOp> { static Status Call(KernelContext* ctx, const ArrayData& left, const ArrayData& right, ArrayData* out) { - ::arrow::internal::BitmapXor(left.buffers[1]->data(), left.offset, - right.buffers[1]->data(), right.offset, right.length, - out->offset, out->buffers[1]->mutable_data()); + ::arrow::internal::BitmapXor(left.buffers[1]->data(), left.offset, + right.buffers[1]->data(), right.offset, right.length, + out->offset, out->buffers[1]->mutable_data()); return Status::OK(); - } -}; - + } +}; + struct AndNotOp { static Status Call(KernelContext* ctx, const Scalar& left, const Scalar& right, Scalar* out) { @@ -458,18 +458,18 @@ struct KleeneAndNotOp { void MakeFunction(const std::string& name, int arity, ArrayKernelExec exec, const FunctionDoc* doc, FunctionRegistry* registry, - NullHandling::type null_handling = NullHandling::INTERSECTION) { + NullHandling::type null_handling = NullHandling::INTERSECTION) { auto func = std::make_shared<ScalarFunction>(name, Arity(arity), doc); - - // Scalar arguments not yet supported + + // Scalar arguments not yet supported std::vector<InputType> in_types(arity, InputType(boolean())); - ScalarKernel kernel(std::move(in_types), boolean(), exec); - kernel.null_handling = null_handling; - - DCHECK_OK(func->AddKernel(kernel)); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + ScalarKernel kernel(std::move(in_types), boolean(), exec); + kernel.null_handling = null_handling; + + DCHECK_OK(func->AddKernel(kernel)); + DCHECK_OK(registry->AddFunction(std::move(func))); +} + const FunctionDoc invert_doc{"Invert boolean values", "", {"values"}}; const FunctionDoc and_doc{ @@ -538,26 +538,26 @@ const FunctionDoc or_kleene_doc{ "For a different null behavior, see function \"and\"."), {"x", "y"}}; -} // namespace - -namespace internal { - -void RegisterScalarBoolean(FunctionRegistry* registry) { - // These functions can write into sliced output bitmaps +} // namespace + +namespace internal { + +void RegisterScalarBoolean(FunctionRegistry* registry) { + // These functions can write into sliced output bitmaps MakeFunction("invert", 1, applicator::SimpleUnary<InvertOp>, &invert_doc, registry); MakeFunction("and", 2, applicator::SimpleBinary<AndOp>, &and_doc, registry); MakeFunction("and_not", 2, applicator::SimpleBinary<AndNotOp>, &and_not_doc, registry); MakeFunction("or", 2, applicator::SimpleBinary<OrOp>, &or_doc, registry); MakeFunction("xor", 2, applicator::SimpleBinary<XorOp>, &xor_doc, registry); - + MakeFunction("and_kleene", 2, applicator::SimpleBinary<KleeneAndOp>, &and_kleene_doc, registry, NullHandling::COMPUTED_PREALLOCATE); MakeFunction("and_not_kleene", 2, applicator::SimpleBinary<KleeneAndNotOp>, &and_not_kleene_doc, registry, NullHandling::COMPUTED_PREALLOCATE); MakeFunction("or_kleene", 2, applicator::SimpleBinary<KleeneOrOp>, &or_kleene_doc, registry, NullHandling::COMPUTED_PREALLOCATE); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc index c5fd7b78b1..dad94c1ace 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_boolean.cc @@ -1,70 +1,70 @@ -// 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. - -// Cast types to boolean - +// 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. + +// Cast types to boolean + #include "arrow/array/builder_primitive.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/scalar_cast_internal.h" -#include "arrow/util/value_parsing.h" - -namespace arrow { - -using internal::ParseValue; - -namespace compute { -namespace internal { - -struct IsNonZero { - template <typename OutValue, typename Arg0Value> +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/util/value_parsing.h" + +namespace arrow { + +using internal::ParseValue; + +namespace compute { +namespace internal { + +struct IsNonZero { + template <typename OutValue, typename Arg0Value> static OutValue Call(KernelContext*, Arg0Value val, Status*) { - return val != 0; - } -}; - -struct ParseBooleanString { - template <typename OutValue, typename Arg0Value> + return val != 0; + } +}; + +struct ParseBooleanString { + template <typename OutValue, typename Arg0Value> static OutValue Call(KernelContext*, Arg0Value val, Status* st) { - bool result = false; - if (ARROW_PREDICT_FALSE(!ParseValue<BooleanType>(val.data(), val.size(), &result))) { + bool result = false; + if (ARROW_PREDICT_FALSE(!ParseValue<BooleanType>(val.data(), val.size(), &result))) { *st = Status::Invalid("Failed to parse value: ", val); - } - return result; - } -}; - -std::vector<std::shared_ptr<CastFunction>> GetBooleanCasts() { - auto func = std::make_shared<CastFunction>("cast_boolean", Type::BOOL); - AddCommonCasts(Type::BOOL, boolean(), func.get()); + } + return result; + } +}; + +std::vector<std::shared_ptr<CastFunction>> GetBooleanCasts() { + auto func = std::make_shared<CastFunction>("cast_boolean", Type::BOOL); + AddCommonCasts(Type::BOOL, boolean(), func.get()); AddZeroCopyCast(Type::BOOL, boolean(), boolean(), func.get()); - - for (const auto& ty : NumericTypes()) { - ArrayKernelExec exec = - GenerateNumeric<applicator::ScalarUnary, BooleanType, IsNonZero>(*ty); - DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec)); - } - for (const auto& ty : BaseBinaryTypes()) { - ArrayKernelExec exec = GenerateVarBinaryBase<applicator::ScalarUnaryNotNull, - BooleanType, ParseBooleanString>(*ty); - DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec)); - } - return {func}; -} - -} // namespace internal -} // namespace compute -} // namespace arrow + + for (const auto& ty : NumericTypes()) { + ArrayKernelExec exec = + GenerateNumeric<applicator::ScalarUnary, BooleanType, IsNonZero>(*ty); + DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec)); + } + for (const auto& ty : BaseBinaryTypes()) { + ArrayKernelExec exec = GenerateVarBinaryBase<applicator::ScalarUnaryNotNull, + BooleanType, ParseBooleanString>(*ty); + DCHECK_OK(func->AddKernel(ty->id(), {ty}, boolean(), exec)); + } + return {func}; +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.cc index e25523a3c1..198c82bd97 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.cc @@ -1,175 +1,175 @@ -// 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. - -#include "arrow/compute/kernels/scalar_cast_internal.h" -#include "arrow/compute/cast_internal.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/extension_type.h" - -namespace arrow { - -using internal::PrimitiveScalarBase; - -namespace compute { -namespace internal { - -// ---------------------------------------------------------------------- - -template <typename OutT, typename InT> -ARROW_DISABLE_UBSAN("float-cast-overflow") -void DoStaticCast(const void* in_data, int64_t in_offset, int64_t length, - int64_t out_offset, void* out_data) { - auto in = reinterpret_cast<const InT*>(in_data) + in_offset; - auto out = reinterpret_cast<OutT*>(out_data) + out_offset; - for (int64_t i = 0; i < length; ++i) { - *out++ = static_cast<OutT>(*in++); - } -} - -using StaticCastFunc = std::function<void(const void*, int64_t, int64_t, int64_t, void*)>; - -template <typename OutType, typename InType, typename Enable = void> -struct CastPrimitive { - static void Exec(const Datum& input, Datum* out) { - using OutT = typename OutType::c_type; - using InT = typename InType::c_type; - - StaticCastFunc caster = DoStaticCast<OutT, InT>; - if (input.kind() == Datum::ARRAY) { - const ArrayData& arr = *input.array(); - ArrayData* out_arr = out->mutable_array(); - caster(arr.buffers[1]->data(), arr.offset, arr.length, out_arr->offset, - out_arr->buffers[1]->mutable_data()); - } else { - // Scalar path. Use the caster with length 1 to place the casted value into - // the output - const auto& in_scalar = input.scalar_as<PrimitiveScalarBase>(); - auto out_scalar = checked_cast<PrimitiveScalarBase*>(out->scalar().get()); - caster(in_scalar.data(), /*in_offset=*/0, /*length=*/1, /*out_offset=*/0, - out_scalar->mutable_data()); - } - } -}; - -template <typename OutType, typename InType> -struct CastPrimitive<OutType, InType, enable_if_t<std::is_same<OutType, InType>::value>> { - // memcpy output - static void Exec(const Datum& input, Datum* out) { - using T = typename InType::c_type; - - if (input.kind() == Datum::ARRAY) { - const ArrayData& arr = *input.array(); - ArrayData* out_arr = out->mutable_array(); - std::memcpy( - reinterpret_cast<T*>(out_arr->buffers[1]->mutable_data()) + out_arr->offset, - reinterpret_cast<const T*>(arr.buffers[1]->data()) + arr.offset, - arr.length * sizeof(T)); - } else { - // Scalar path. Use the caster with length 1 to place the casted value into - // the output - const auto& in_scalar = input.scalar_as<PrimitiveScalarBase>(); - auto out_scalar = checked_cast<PrimitiveScalarBase*>(out->scalar().get()); - *reinterpret_cast<T*>(out_scalar->mutable_data()) = - *reinterpret_cast<const T*>(in_scalar.data()); - } - } -}; - -template <typename InType> -void CastNumberImpl(Type::type out_type, const Datum& input, Datum* out) { - switch (out_type) { - case Type::INT8: - return CastPrimitive<Int8Type, InType>::Exec(input, out); - case Type::INT16: - return CastPrimitive<Int16Type, InType>::Exec(input, out); - case Type::INT32: - return CastPrimitive<Int32Type, InType>::Exec(input, out); - case Type::INT64: - return CastPrimitive<Int64Type, InType>::Exec(input, out); - case Type::UINT8: - return CastPrimitive<UInt8Type, InType>::Exec(input, out); - case Type::UINT16: - return CastPrimitive<UInt16Type, InType>::Exec(input, out); - case Type::UINT32: - return CastPrimitive<UInt32Type, InType>::Exec(input, out); - case Type::UINT64: - return CastPrimitive<UInt64Type, InType>::Exec(input, out); - case Type::FLOAT: - return CastPrimitive<FloatType, InType>::Exec(input, out); - case Type::DOUBLE: - return CastPrimitive<DoubleType, InType>::Exec(input, out); - default: - break; - } -} - -void CastNumberToNumberUnsafe(Type::type in_type, Type::type out_type, const Datum& input, - Datum* out) { - switch (in_type) { - case Type::INT8: - return CastNumberImpl<Int8Type>(out_type, input, out); - case Type::INT16: - return CastNumberImpl<Int16Type>(out_type, input, out); - case Type::INT32: - return CastNumberImpl<Int32Type>(out_type, input, out); - case Type::INT64: - return CastNumberImpl<Int64Type>(out_type, input, out); - case Type::UINT8: - return CastNumberImpl<UInt8Type>(out_type, input, out); - case Type::UINT16: - return CastNumberImpl<UInt16Type>(out_type, input, out); - case Type::UINT32: - return CastNumberImpl<UInt32Type>(out_type, input, out); - case Type::UINT64: - return CastNumberImpl<UInt64Type>(out_type, input, out); - case Type::FLOAT: - return CastNumberImpl<FloatType>(out_type, input, out); - case Type::DOUBLE: - return CastNumberImpl<DoubleType>(out_type, input, out); - default: - DCHECK(false); - break; - } -} - -// ---------------------------------------------------------------------- - +// 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. + +#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/compute/cast_internal.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/extension_type.h" + +namespace arrow { + +using internal::PrimitiveScalarBase; + +namespace compute { +namespace internal { + +// ---------------------------------------------------------------------- + +template <typename OutT, typename InT> +ARROW_DISABLE_UBSAN("float-cast-overflow") +void DoStaticCast(const void* in_data, int64_t in_offset, int64_t length, + int64_t out_offset, void* out_data) { + auto in = reinterpret_cast<const InT*>(in_data) + in_offset; + auto out = reinterpret_cast<OutT*>(out_data) + out_offset; + for (int64_t i = 0; i < length; ++i) { + *out++ = static_cast<OutT>(*in++); + } +} + +using StaticCastFunc = std::function<void(const void*, int64_t, int64_t, int64_t, void*)>; + +template <typename OutType, typename InType, typename Enable = void> +struct CastPrimitive { + static void Exec(const Datum& input, Datum* out) { + using OutT = typename OutType::c_type; + using InT = typename InType::c_type; + + StaticCastFunc caster = DoStaticCast<OutT, InT>; + if (input.kind() == Datum::ARRAY) { + const ArrayData& arr = *input.array(); + ArrayData* out_arr = out->mutable_array(); + caster(arr.buffers[1]->data(), arr.offset, arr.length, out_arr->offset, + out_arr->buffers[1]->mutable_data()); + } else { + // Scalar path. Use the caster with length 1 to place the casted value into + // the output + const auto& in_scalar = input.scalar_as<PrimitiveScalarBase>(); + auto out_scalar = checked_cast<PrimitiveScalarBase*>(out->scalar().get()); + caster(in_scalar.data(), /*in_offset=*/0, /*length=*/1, /*out_offset=*/0, + out_scalar->mutable_data()); + } + } +}; + +template <typename OutType, typename InType> +struct CastPrimitive<OutType, InType, enable_if_t<std::is_same<OutType, InType>::value>> { + // memcpy output + static void Exec(const Datum& input, Datum* out) { + using T = typename InType::c_type; + + if (input.kind() == Datum::ARRAY) { + const ArrayData& arr = *input.array(); + ArrayData* out_arr = out->mutable_array(); + std::memcpy( + reinterpret_cast<T*>(out_arr->buffers[1]->mutable_data()) + out_arr->offset, + reinterpret_cast<const T*>(arr.buffers[1]->data()) + arr.offset, + arr.length * sizeof(T)); + } else { + // Scalar path. Use the caster with length 1 to place the casted value into + // the output + const auto& in_scalar = input.scalar_as<PrimitiveScalarBase>(); + auto out_scalar = checked_cast<PrimitiveScalarBase*>(out->scalar().get()); + *reinterpret_cast<T*>(out_scalar->mutable_data()) = + *reinterpret_cast<const T*>(in_scalar.data()); + } + } +}; + +template <typename InType> +void CastNumberImpl(Type::type out_type, const Datum& input, Datum* out) { + switch (out_type) { + case Type::INT8: + return CastPrimitive<Int8Type, InType>::Exec(input, out); + case Type::INT16: + return CastPrimitive<Int16Type, InType>::Exec(input, out); + case Type::INT32: + return CastPrimitive<Int32Type, InType>::Exec(input, out); + case Type::INT64: + return CastPrimitive<Int64Type, InType>::Exec(input, out); + case Type::UINT8: + return CastPrimitive<UInt8Type, InType>::Exec(input, out); + case Type::UINT16: + return CastPrimitive<UInt16Type, InType>::Exec(input, out); + case Type::UINT32: + return CastPrimitive<UInt32Type, InType>::Exec(input, out); + case Type::UINT64: + return CastPrimitive<UInt64Type, InType>::Exec(input, out); + case Type::FLOAT: + return CastPrimitive<FloatType, InType>::Exec(input, out); + case Type::DOUBLE: + return CastPrimitive<DoubleType, InType>::Exec(input, out); + default: + break; + } +} + +void CastNumberToNumberUnsafe(Type::type in_type, Type::type out_type, const Datum& input, + Datum* out) { + switch (in_type) { + case Type::INT8: + return CastNumberImpl<Int8Type>(out_type, input, out); + case Type::INT16: + return CastNumberImpl<Int16Type>(out_type, input, out); + case Type::INT32: + return CastNumberImpl<Int32Type>(out_type, input, out); + case Type::INT64: + return CastNumberImpl<Int64Type>(out_type, input, out); + case Type::UINT8: + return CastNumberImpl<UInt8Type>(out_type, input, out); + case Type::UINT16: + return CastNumberImpl<UInt16Type>(out_type, input, out); + case Type::UINT32: + return CastNumberImpl<UInt32Type>(out_type, input, out); + case Type::UINT64: + return CastNumberImpl<UInt64Type>(out_type, input, out); + case Type::FLOAT: + return CastNumberImpl<FloatType>(out_type, input, out); + case Type::DOUBLE: + return CastNumberImpl<DoubleType>(out_type, input, out); + default: + DCHECK(false); + break; + } +} + +// ---------------------------------------------------------------------- + Status UnpackDictionary(KernelContext* ctx, const ExecBatch& batch, Datum* out) { DCHECK(out->is_array()); - DictionaryArray dict_arr(batch[0].array()); - const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; - - const auto& dict_type = *dict_arr.dictionary()->type(); + DictionaryArray dict_arr(batch[0].array()); + const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; + + const auto& dict_type = *dict_arr.dictionary()->type(); if (!dict_type.Equals(options.to_type) && !CanCast(dict_type, *options.to_type)) { return Status::Invalid("Cast type ", options.to_type->ToString(), " incompatible with dictionary type ", dict_type.ToString()); - } - + } + ARROW_ASSIGN_OR_RAISE(*out, Take(Datum(dict_arr.dictionary()), Datum(dict_arr.indices()), TakeOptions::Defaults(), ctx->exec_context())); if (!dict_type.Equals(options.to_type)) { ARROW_ASSIGN_OR_RAISE(*out, Cast(*out, options)); - } + } return Status::OK(); -} - +} + Status OutputAllNull(KernelContext* ctx, const ExecBatch& batch, Datum* out) { if (out->is_scalar()) { out->scalar()->is_valid = false; @@ -179,23 +179,23 @@ Status OutputAllNull(KernelContext* ctx, const ExecBatch& batch, Datum* out) { output->null_count = batch.length; } return Status::OK(); -} - +} + Status CastFromExtension(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const CastOptions& options = checked_cast<const CastState*>(ctx->state())->options; - - const DataType& in_type = *batch[0].type(); - const auto storage_type = checked_cast<const ExtensionType&>(in_type).storage_type(); - - ExtensionArray extension(batch[0].array()); - - Datum casted_storage; + const CastOptions& options = checked_cast<const CastState*>(ctx->state())->options; + + const DataType& in_type = *batch[0].type(); + const auto storage_type = checked_cast<const ExtensionType&>(in_type).storage_type(); + + ExtensionArray extension(batch[0].array()); + + Datum casted_storage; RETURN_NOT_OK(Cast(*extension.storage(), out->type(), options, ctx->exec_context()) .Value(&casted_storage)); - out->value = casted_storage.array(); + out->value = casted_storage.array(); return Status::OK(); -} - +} + Status CastFromNull(KernelContext* ctx, const ExecBatch& batch, Datum* out) { if (!batch[0].is_scalar()) { ArrayData* output = out->mutable_array(); @@ -204,25 +204,25 @@ Status CastFromNull(KernelContext* ctx, const ExecBatch& batch, Datum* out) { out->value = nulls->data(); } return Status::OK(); -} - -Result<ValueDescr> ResolveOutputFromOptions(KernelContext* ctx, - const std::vector<ValueDescr>& args) { - const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; - return ValueDescr(options.to_type, args[0].shape); -} - -/// You will see some of kernels with -/// -/// kOutputTargetType -/// -/// for their output type resolution. This is somewhat of an eyesore but the -/// easiest initial way to get the requested cast type including the TimeUnit -/// to the kernel (which is needed to compute the output) was through -/// CastOptions - -OutputType kOutputTargetType(ResolveOutputFromOptions); - +} + +Result<ValueDescr> ResolveOutputFromOptions(KernelContext* ctx, + const std::vector<ValueDescr>& args) { + const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; + return ValueDescr(options.to_type, args[0].shape); +} + +/// You will see some of kernels with +/// +/// kOutputTargetType +/// +/// for their output type resolution. This is somewhat of an eyesore but the +/// easiest initial way to get the requested cast type including the TimeUnit +/// to the kernel (which is needed to compute the output) was through +/// CastOptions + +OutputType kOutputTargetType(ResolveOutputFromOptions); + Status ZeroCopyCastExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { DCHECK_EQ(batch[0].kind(), Datum::ARRAY); // Make a copy of the buffers into a destination array without carrying @@ -235,51 +235,51 @@ Status ZeroCopyCastExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) output->offset = input.offset; output->child_data = input.child_data; return Status::OK(); -} - -void AddZeroCopyCast(Type::type in_type_id, InputType in_type, OutputType out_type, - CastFunction* func) { - auto sig = KernelSignature::Make({in_type}, out_type); - ScalarKernel kernel; +} + +void AddZeroCopyCast(Type::type in_type_id, InputType in_type, OutputType out_type, + CastFunction* func) { + auto sig = KernelSignature::Make({in_type}, out_type); + ScalarKernel kernel; kernel.exec = TrivialScalarUnaryAsArraysExec(ZeroCopyCastExec); - kernel.signature = sig; - kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; - kernel.mem_allocation = MemAllocation::NO_PREALLOCATE; - DCHECK_OK(func->AddKernel(in_type_id, std::move(kernel))); -} - -static bool CanCastFromDictionary(Type::type type_id) { - return (is_primitive(type_id) || is_base_binary_like(type_id) || - is_fixed_size_binary(type_id)); -} - -void AddCommonCasts(Type::type out_type_id, OutputType out_ty, CastFunction* func) { - // From null to this type + kernel.signature = sig; + kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; + kernel.mem_allocation = MemAllocation::NO_PREALLOCATE; + DCHECK_OK(func->AddKernel(in_type_id, std::move(kernel))); +} + +static bool CanCastFromDictionary(Type::type type_id) { + return (is_primitive(type_id) || is_base_binary_like(type_id) || + is_fixed_size_binary(type_id)); +} + +void AddCommonCasts(Type::type out_type_id, OutputType out_ty, CastFunction* func) { + // From null to this type ScalarKernel kernel; kernel.exec = CastFromNull; kernel.signature = KernelSignature::Make({null()}, out_ty); kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; kernel.mem_allocation = MemAllocation::NO_PREALLOCATE; DCHECK_OK(func->AddKernel(Type::NA, std::move(kernel))); - - // From dictionary to this type - if (CanCastFromDictionary(out_type_id)) { - // Dictionary unpacking not implemented for boolean or nested types. - // - // XXX: Uses Take and does its own memory allocation for the moment. We can - // fix this later. + + // From dictionary to this type + if (CanCastFromDictionary(out_type_id)) { + // Dictionary unpacking not implemented for boolean or nested types. + // + // XXX: Uses Take and does its own memory allocation for the moment. We can + // fix this later. DCHECK_OK(func->AddKernel(Type::DICTIONARY, {InputType(Type::DICTIONARY)}, out_ty, TrivialScalarUnaryAsArraysExec(UnpackDictionary), NullHandling::COMPUTED_NO_PREALLOCATE, MemAllocation::NO_PREALLOCATE)); - } - - // From extension type to this type - DCHECK_OK(func->AddKernel(Type::EXTENSION, {InputType::Array(Type::EXTENSION)}, out_ty, - CastFromExtension, NullHandling::COMPUTED_NO_PREALLOCATE, - MemAllocation::NO_PREALLOCATE)); -} - -} // namespace internal -} // namespace compute -} // namespace arrow + } + + // From extension type to this type + DCHECK_OK(func->AddKernel(Type::EXTENSION, {InputType::Array(Type::EXTENSION)}, out_ty, + CastFromExtension, NullHandling::COMPUTED_NO_PREALLOCATE, + MemAllocation::NO_PREALLOCATE)); +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.h index 12e3605695..2419d898a6 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_internal.h @@ -1,88 +1,88 @@ -// 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 "arrow/compute/api_vector.h" -#include "arrow/compute/cast.h" // IWYU pragma: export -#include "arrow/compute/cast_internal.h" // IWYU pragma: export -#include "arrow/compute/kernels/common.h" +// 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 "arrow/compute/api_vector.h" +#include "arrow/compute/cast.h" // IWYU pragma: export +#include "arrow/compute/cast_internal.h" // IWYU pragma: export +#include "arrow/compute/kernels/common.h" #include "arrow/compute/kernels/util_internal.h" - -namespace arrow { - -using internal::checked_cast; - -namespace compute { -namespace internal { - -template <typename OutType, typename InType, typename Enable = void> -struct CastFunctor {}; - -// No-op functor for identity casts -template <typename O, typename I> -struct CastFunctor< - O, I, enable_if_t<std::is_same<O, I>::value && is_parameter_free_type<I>::value>> { + +namespace arrow { + +using internal::checked_cast; + +namespace compute { +namespace internal { + +template <typename OutType, typename InType, typename Enable = void> +struct CastFunctor {}; + +// No-op functor for identity casts +template <typename O, typename I> +struct CastFunctor< + O, I, enable_if_t<std::is_same<O, I>::value && is_parameter_free_type<I>::value>> { static Status Exec(KernelContext*, const ExecBatch&, Datum*) { return Status::OK(); } -}; - +}; + Status CastFromExtension(KernelContext* ctx, const ExecBatch& batch, Datum* out); - -// Utility for numeric casts -void CastNumberToNumberUnsafe(Type::type in_type, Type::type out_type, const Datum& input, - Datum* out); - -// ---------------------------------------------------------------------- -// Dictionary to other things - + +// Utility for numeric casts +void CastNumberToNumberUnsafe(Type::type in_type, Type::type out_type, const Datum& input, + Datum* out); + +// ---------------------------------------------------------------------- +// Dictionary to other things + Status UnpackDictionary(KernelContext* ctx, const ExecBatch& batch, Datum* out); - + Status OutputAllNull(KernelContext* ctx, const ExecBatch& batch, Datum* out); - + Status CastFromNull(KernelContext* ctx, const ExecBatch& batch, Datum* out); - + // Adds a cast function where CastFunctor is specialized and the input and output // types are parameter free (have a type_singleton). Scalar inputs are handled by // wrapping with TrivialScalarUnaryAsArraysExec. -template <typename InType, typename OutType> -void AddSimpleCast(InputType in_ty, OutputType out_ty, CastFunction* func) { +template <typename InType, typename OutType> +void AddSimpleCast(InputType in_ty, OutputType out_ty, CastFunction* func) { DCHECK_OK(func->AddKernel( InType::type_id, {in_ty}, out_ty, TrivialScalarUnaryAsArraysExec(CastFunctor<OutType, InType>::Exec))); -} - +} + Status ZeroCopyCastExec(KernelContext* ctx, const ExecBatch& batch, Datum* out); - -void AddZeroCopyCast(Type::type in_type_id, InputType in_type, OutputType out_type, - CastFunction* func); - -// OutputType::Resolver that returns a descr with the shape of the input -// argument and the type from CastOptions -Result<ValueDescr> ResolveOutputFromOptions(KernelContext* ctx, - const std::vector<ValueDescr>& args); - -ARROW_EXPORT extern OutputType kOutputTargetType; - -// Add generic casts to out_ty from: -// - the null type -// - dictionary with out_ty as given value type -// - extension types with a compatible storage type -void AddCommonCasts(Type::type out_type_id, OutputType out_ty, CastFunction* func); - -} // namespace internal -} // namespace compute -} // namespace arrow + +void AddZeroCopyCast(Type::type in_type_id, InputType in_type, OutputType out_type, + CastFunction* func); + +// OutputType::Resolver that returns a descr with the shape of the input +// argument and the type from CastOptions +Result<ValueDescr> ResolveOutputFromOptions(KernelContext* ctx, + const std::vector<ValueDescr>& args); + +ARROW_EXPORT extern OutputType kOutputTargetType; + +// Add generic casts to out_ty from: +// - the null type +// - dictionary with out_ty as given value type +// - extension types with a compatible storage type +void AddCommonCasts(Type::type out_type_id, OutputType out_ty, CastFunction* func); + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_nested.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_nested.cc index 8b8fdf094a..ec92dbb5d6 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_nested.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_nested.cc @@ -1,46 +1,46 @@ -// 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. - -// Implementation of casting to (or between) list types - -#include <utility> -#include <vector> - +// 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. + +// Implementation of casting to (or between) list types + +#include <utility> +#include <vector> + #include "arrow/array/builder_nested.h" #include "arrow/compute/api_scalar.h" -#include "arrow/compute/cast.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/compute/cast.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/scalar_cast_internal.h" #include "arrow/util/bitmap_ops.h" - -namespace arrow { + +namespace arrow { using internal::CopyBitmap; -namespace compute { -namespace internal { - -template <typename Type> +namespace compute { +namespace internal { + +template <typename Type> Status CastListExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { using offset_type = typename Type::offset_type; using ScalarType = typename TypeTraits<Type>::ScalarType; - + const CastOptions& options = CastState::Get(ctx); - + auto child_type = checked_cast<const Type&>(*out->type()).value_type(); if (out->kind() == Datum::SCALAR) { @@ -55,11 +55,11 @@ Status CastListExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { out_scalar->is_valid = true; } return Status::OK(); - } - + } + const ArrayData& in_array = *batch[0].array(); ArrayData* out_array = out->mutable_array(); - + // Copy from parent out_array->buffers = in_array.buffers; Datum values = in_array.child_data[0]; @@ -88,46 +88,46 @@ Status CastListExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { DCHECK_EQ(Datum::ARRAY, cast_values.kind()); out_array->child_data.push_back(cast_values.array()); return Status::OK(); -} - -template <typename Type> -void AddListCast(CastFunction* func) { - ScalarKernel kernel; - kernel.exec = CastListExec<Type>; - kernel.signature = KernelSignature::Make({InputType(Type::type_id)}, kOutputTargetType); - kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; - DCHECK_OK(func->AddKernel(Type::type_id, std::move(kernel))); -} - -std::vector<std::shared_ptr<CastFunction>> GetNestedCasts() { - // We use the list<T> from the CastOptions when resolving the output type - - auto cast_list = std::make_shared<CastFunction>("cast_list", Type::LIST); - AddCommonCasts(Type::LIST, kOutputTargetType, cast_list.get()); - AddListCast<ListType>(cast_list.get()); - - auto cast_large_list = - std::make_shared<CastFunction>("cast_large_list", Type::LARGE_LIST); - AddCommonCasts(Type::LARGE_LIST, kOutputTargetType, cast_large_list.get()); - AddListCast<LargeListType>(cast_large_list.get()); - - // FSL is a bit incomplete at the moment - auto cast_fsl = - std::make_shared<CastFunction>("cast_fixed_size_list", Type::FIXED_SIZE_LIST); - AddCommonCasts(Type::FIXED_SIZE_LIST, kOutputTargetType, cast_fsl.get()); - - // So is struct - auto cast_struct = std::make_shared<CastFunction>("cast_struct", Type::STRUCT); - AddCommonCasts(Type::STRUCT, kOutputTargetType, cast_struct.get()); - +} + +template <typename Type> +void AddListCast(CastFunction* func) { + ScalarKernel kernel; + kernel.exec = CastListExec<Type>; + kernel.signature = KernelSignature::Make({InputType(Type::type_id)}, kOutputTargetType); + kernel.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; + DCHECK_OK(func->AddKernel(Type::type_id, std::move(kernel))); +} + +std::vector<std::shared_ptr<CastFunction>> GetNestedCasts() { + // We use the list<T> from the CastOptions when resolving the output type + + auto cast_list = std::make_shared<CastFunction>("cast_list", Type::LIST); + AddCommonCasts(Type::LIST, kOutputTargetType, cast_list.get()); + AddListCast<ListType>(cast_list.get()); + + auto cast_large_list = + std::make_shared<CastFunction>("cast_large_list", Type::LARGE_LIST); + AddCommonCasts(Type::LARGE_LIST, kOutputTargetType, cast_large_list.get()); + AddListCast<LargeListType>(cast_large_list.get()); + + // FSL is a bit incomplete at the moment + auto cast_fsl = + std::make_shared<CastFunction>("cast_fixed_size_list", Type::FIXED_SIZE_LIST); + AddCommonCasts(Type::FIXED_SIZE_LIST, kOutputTargetType, cast_fsl.get()); + + // So is struct + auto cast_struct = std::make_shared<CastFunction>("cast_struct", Type::STRUCT); + AddCommonCasts(Type::STRUCT, kOutputTargetType, cast_struct.get()); + // So is dictionary auto cast_dictionary = std::make_shared<CastFunction>("cast_dictionary", Type::DICTIONARY); AddCommonCasts(Type::DICTIONARY, kOutputTargetType, cast_dictionary.get()); return {cast_list, cast_large_list, cast_fsl, cast_struct, cast_dictionary}; -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc index ae9a04e8e9..cc7b533f26 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_numeric.cc @@ -1,399 +1,399 @@ -// 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. - -// Implementation of casting to integer, floating point, or decimal types - +// 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. + +// Implementation of casting to integer, floating point, or decimal types + #include "arrow/array/builder_primitive.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/scalar_cast_internal.h" #include "arrow/compute/kernels/util_internal.h" -#include "arrow/util/bit_block_counter.h" -#include "arrow/util/int_util.h" -#include "arrow/util/value_parsing.h" - -namespace arrow { - -using internal::BitBlockCount; -using internal::CheckIntegersInRange; -using internal::IntegersCanFit; -using internal::OptionalBitBlockCounter; -using internal::ParseValue; - -namespace compute { -namespace internal { - +#include "arrow/util/bit_block_counter.h" +#include "arrow/util/int_util.h" +#include "arrow/util/value_parsing.h" + +namespace arrow { + +using internal::BitBlockCount; +using internal::CheckIntegersInRange; +using internal::IntegersCanFit; +using internal::OptionalBitBlockCounter; +using internal::ParseValue; + +namespace compute { +namespace internal { + Status CastIntegerToInteger(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; - if (!options.allow_int_overflow) { + const auto& options = checked_cast<const CastState*>(ctx->state())->options; + if (!options.allow_int_overflow) { RETURN_NOT_OK(IntegersCanFit(batch[0], *out->type())); - } - CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); + } + CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); return Status::OK(); -} - +} + Status CastFloatingToFloating(KernelContext*, const ExecBatch& batch, Datum* out) { - CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); + CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Implement fast safe floating point to integer cast - -// InType is a floating point type we are planning to cast to integer -template <typename InType, typename OutType, typename InT = typename InType::c_type, - typename OutT = typename OutType::c_type> -ARROW_DISABLE_UBSAN("float-cast-overflow") -Status CheckFloatTruncation(const Datum& input, const Datum& output) { - auto WasTruncated = [&](OutT out_val, InT in_val) -> bool { - return static_cast<InT>(out_val) != in_val; - }; - auto WasTruncatedMaybeNull = [&](OutT out_val, InT in_val, bool is_valid) -> bool { - return is_valid && static_cast<InT>(out_val) != in_val; - }; - auto GetErrorMessage = [&](InT val) { +} + +// ---------------------------------------------------------------------- +// Implement fast safe floating point to integer cast + +// InType is a floating point type we are planning to cast to integer +template <typename InType, typename OutType, typename InT = typename InType::c_type, + typename OutT = typename OutType::c_type> +ARROW_DISABLE_UBSAN("float-cast-overflow") +Status CheckFloatTruncation(const Datum& input, const Datum& output) { + auto WasTruncated = [&](OutT out_val, InT in_val) -> bool { + return static_cast<InT>(out_val) != in_val; + }; + auto WasTruncatedMaybeNull = [&](OutT out_val, InT in_val, bool is_valid) -> bool { + return is_valid && static_cast<InT>(out_val) != in_val; + }; + auto GetErrorMessage = [&](InT val) { return Status::Invalid("Float value ", val, " was truncated converting to ", - *output.type()); - }; - - if (input.kind() == Datum::SCALAR) { - DCHECK_EQ(output.kind(), Datum::SCALAR); - const auto& in_scalar = input.scalar_as<typename TypeTraits<InType>::ScalarType>(); - const auto& out_scalar = output.scalar_as<typename TypeTraits<OutType>::ScalarType>(); - if (WasTruncatedMaybeNull(out_scalar.value, in_scalar.value, out_scalar.is_valid)) { - return GetErrorMessage(in_scalar.value); - } - return Status::OK(); - } - - const ArrayData& in_array = *input.array(); - const ArrayData& out_array = *output.array(); - - const InT* in_data = in_array.GetValues<InT>(1); - const OutT* out_data = out_array.GetValues<OutT>(1); - - const uint8_t* bitmap = nullptr; - if (in_array.buffers[0]) { - bitmap = in_array.buffers[0]->data(); - } - OptionalBitBlockCounter bit_counter(bitmap, in_array.offset, in_array.length); - int64_t position = 0; - int64_t offset_position = in_array.offset; - while (position < in_array.length) { - BitBlockCount block = bit_counter.NextBlock(); - bool block_out_of_bounds = false; - if (block.popcount == block.length) { - // Fast path: branchless - for (int64_t i = 0; i < block.length; ++i) { - block_out_of_bounds |= WasTruncated(out_data[i], in_data[i]); - } - } else if (block.popcount > 0) { - // Indices have nulls, must only boundscheck non-null values - for (int64_t i = 0; i < block.length; ++i) { - block_out_of_bounds |= WasTruncatedMaybeNull( - out_data[i], in_data[i], BitUtil::GetBit(bitmap, offset_position + i)); - } - } - if (ARROW_PREDICT_FALSE(block_out_of_bounds)) { - if (in_array.GetNullCount() > 0) { - for (int64_t i = 0; i < block.length; ++i) { - if (WasTruncatedMaybeNull(out_data[i], in_data[i], - BitUtil::GetBit(bitmap, offset_position + i))) { - return GetErrorMessage(in_data[i]); - } - } - } else { - for (int64_t i = 0; i < block.length; ++i) { - if (WasTruncated(out_data[i], in_data[i])) { - return GetErrorMessage(in_data[i]); - } - } - } - } - in_data += block.length; - out_data += block.length; - position += block.length; - offset_position += block.length; - } - return Status::OK(); -} - -template <typename InType> -Status CheckFloatToIntTruncationImpl(const Datum& input, const Datum& output) { - switch (output.type()->id()) { - case Type::INT8: - return CheckFloatTruncation<InType, Int8Type>(input, output); - case Type::INT16: - return CheckFloatTruncation<InType, Int16Type>(input, output); - case Type::INT32: - return CheckFloatTruncation<InType, Int32Type>(input, output); - case Type::INT64: - return CheckFloatTruncation<InType, Int64Type>(input, output); - case Type::UINT8: - return CheckFloatTruncation<InType, UInt8Type>(input, output); - case Type::UINT16: - return CheckFloatTruncation<InType, UInt16Type>(input, output); - case Type::UINT32: - return CheckFloatTruncation<InType, UInt32Type>(input, output); - case Type::UINT64: - return CheckFloatTruncation<InType, UInt64Type>(input, output); - default: - break; - } - DCHECK(false); - return Status::OK(); -} - -Status CheckFloatToIntTruncation(const Datum& input, const Datum& output) { - switch (input.type()->id()) { - case Type::FLOAT: - return CheckFloatToIntTruncationImpl<FloatType>(input, output); - case Type::DOUBLE: - return CheckFloatToIntTruncationImpl<DoubleType>(input, output); - default: - break; - } - DCHECK(false); - return Status::OK(); -} - + *output.type()); + }; + + if (input.kind() == Datum::SCALAR) { + DCHECK_EQ(output.kind(), Datum::SCALAR); + const auto& in_scalar = input.scalar_as<typename TypeTraits<InType>::ScalarType>(); + const auto& out_scalar = output.scalar_as<typename TypeTraits<OutType>::ScalarType>(); + if (WasTruncatedMaybeNull(out_scalar.value, in_scalar.value, out_scalar.is_valid)) { + return GetErrorMessage(in_scalar.value); + } + return Status::OK(); + } + + const ArrayData& in_array = *input.array(); + const ArrayData& out_array = *output.array(); + + const InT* in_data = in_array.GetValues<InT>(1); + const OutT* out_data = out_array.GetValues<OutT>(1); + + const uint8_t* bitmap = nullptr; + if (in_array.buffers[0]) { + bitmap = in_array.buffers[0]->data(); + } + OptionalBitBlockCounter bit_counter(bitmap, in_array.offset, in_array.length); + int64_t position = 0; + int64_t offset_position = in_array.offset; + while (position < in_array.length) { + BitBlockCount block = bit_counter.NextBlock(); + bool block_out_of_bounds = false; + if (block.popcount == block.length) { + // Fast path: branchless + for (int64_t i = 0; i < block.length; ++i) { + block_out_of_bounds |= WasTruncated(out_data[i], in_data[i]); + } + } else if (block.popcount > 0) { + // Indices have nulls, must only boundscheck non-null values + for (int64_t i = 0; i < block.length; ++i) { + block_out_of_bounds |= WasTruncatedMaybeNull( + out_data[i], in_data[i], BitUtil::GetBit(bitmap, offset_position + i)); + } + } + if (ARROW_PREDICT_FALSE(block_out_of_bounds)) { + if (in_array.GetNullCount() > 0) { + for (int64_t i = 0; i < block.length; ++i) { + if (WasTruncatedMaybeNull(out_data[i], in_data[i], + BitUtil::GetBit(bitmap, offset_position + i))) { + return GetErrorMessage(in_data[i]); + } + } + } else { + for (int64_t i = 0; i < block.length; ++i) { + if (WasTruncated(out_data[i], in_data[i])) { + return GetErrorMessage(in_data[i]); + } + } + } + } + in_data += block.length; + out_data += block.length; + position += block.length; + offset_position += block.length; + } + return Status::OK(); +} + +template <typename InType> +Status CheckFloatToIntTruncationImpl(const Datum& input, const Datum& output) { + switch (output.type()->id()) { + case Type::INT8: + return CheckFloatTruncation<InType, Int8Type>(input, output); + case Type::INT16: + return CheckFloatTruncation<InType, Int16Type>(input, output); + case Type::INT32: + return CheckFloatTruncation<InType, Int32Type>(input, output); + case Type::INT64: + return CheckFloatTruncation<InType, Int64Type>(input, output); + case Type::UINT8: + return CheckFloatTruncation<InType, UInt8Type>(input, output); + case Type::UINT16: + return CheckFloatTruncation<InType, UInt16Type>(input, output); + case Type::UINT32: + return CheckFloatTruncation<InType, UInt32Type>(input, output); + case Type::UINT64: + return CheckFloatTruncation<InType, UInt64Type>(input, output); + default: + break; + } + DCHECK(false); + return Status::OK(); +} + +Status CheckFloatToIntTruncation(const Datum& input, const Datum& output) { + switch (input.type()->id()) { + case Type::FLOAT: + return CheckFloatToIntTruncationImpl<FloatType>(input, output); + case Type::DOUBLE: + return CheckFloatToIntTruncationImpl<DoubleType>(input, output); + default: + break; + } + DCHECK(false); + return Status::OK(); +} + Status CastFloatingToInteger(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; - CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); - if (!options.allow_float_truncate) { + const auto& options = checked_cast<const CastState*>(ctx->state())->options; + CastNumberToNumberUnsafe(batch[0].type()->id(), out->type()->id(), batch[0], out); + if (!options.allow_float_truncate) { RETURN_NOT_OK(CheckFloatToIntTruncation(batch[0], *out)); - } + } + return Status::OK(); +} + +// ---------------------------------------------------------------------- +// Implement fast integer to floating point cast + +// These are the limits for exact representation of whole numbers in floating +// point numbers +template <typename T> +struct FloatingIntegerBound {}; + +template <> +struct FloatingIntegerBound<float> { + static const int64_t value = 1LL << 24; +}; + +template <> +struct FloatingIntegerBound<double> { + static const int64_t value = 1LL << 53; +}; + +template <typename InType, typename OutType, typename InT = typename InType::c_type, + typename OutT = typename OutType::c_type, + bool IsSigned = is_signed_integer_type<InType>::value> +Status CheckIntegerFloatTruncateImpl(const Datum& input) { + using InScalarType = typename TypeTraits<InType>::ScalarType; + const int64_t limit = FloatingIntegerBound<OutT>::value; + InScalarType bound_lower(IsSigned ? -limit : 0); + InScalarType bound_upper(limit); + return CheckIntegersInRange(input, bound_lower, bound_upper); +} + +Status CheckForIntegerToFloatingTruncation(const Datum& input, Type::type out_type) { + switch (input.type()->id()) { + // Small integers are all exactly representable as whole numbers + case Type::INT8: + case Type::INT16: + case Type::UINT8: + case Type::UINT16: + return Status::OK(); + case Type::INT32: { + if (out_type == Type::DOUBLE) { + return Status::OK(); + } + return CheckIntegerFloatTruncateImpl<Int32Type, FloatType>(input); + } + case Type::UINT32: { + if (out_type == Type::DOUBLE) { + return Status::OK(); + } + return CheckIntegerFloatTruncateImpl<UInt32Type, FloatType>(input); + } + case Type::INT64: { + if (out_type == Type::FLOAT) { + return CheckIntegerFloatTruncateImpl<Int64Type, FloatType>(input); + } else { + return CheckIntegerFloatTruncateImpl<Int64Type, DoubleType>(input); + } + } + case Type::UINT64: { + if (out_type == Type::FLOAT) { + return CheckIntegerFloatTruncateImpl<UInt64Type, FloatType>(input); + } else { + return CheckIntegerFloatTruncateImpl<UInt64Type, DoubleType>(input); + } + } + default: + break; + } + DCHECK(false); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Implement fast integer to floating point cast - -// These are the limits for exact representation of whole numbers in floating -// point numbers -template <typename T> -struct FloatingIntegerBound {}; - -template <> -struct FloatingIntegerBound<float> { - static const int64_t value = 1LL << 24; -}; - -template <> -struct FloatingIntegerBound<double> { - static const int64_t value = 1LL << 53; -}; - -template <typename InType, typename OutType, typename InT = typename InType::c_type, - typename OutT = typename OutType::c_type, - bool IsSigned = is_signed_integer_type<InType>::value> -Status CheckIntegerFloatTruncateImpl(const Datum& input) { - using InScalarType = typename TypeTraits<InType>::ScalarType; - const int64_t limit = FloatingIntegerBound<OutT>::value; - InScalarType bound_lower(IsSigned ? -limit : 0); - InScalarType bound_upper(limit); - return CheckIntegersInRange(input, bound_lower, bound_upper); -} - -Status CheckForIntegerToFloatingTruncation(const Datum& input, Type::type out_type) { - switch (input.type()->id()) { - // Small integers are all exactly representable as whole numbers - case Type::INT8: - case Type::INT16: - case Type::UINT8: - case Type::UINT16: - return Status::OK(); - case Type::INT32: { - if (out_type == Type::DOUBLE) { - return Status::OK(); - } - return CheckIntegerFloatTruncateImpl<Int32Type, FloatType>(input); - } - case Type::UINT32: { - if (out_type == Type::DOUBLE) { - return Status::OK(); - } - return CheckIntegerFloatTruncateImpl<UInt32Type, FloatType>(input); - } - case Type::INT64: { - if (out_type == Type::FLOAT) { - return CheckIntegerFloatTruncateImpl<Int64Type, FloatType>(input); - } else { - return CheckIntegerFloatTruncateImpl<Int64Type, DoubleType>(input); - } - } - case Type::UINT64: { - if (out_type == Type::FLOAT) { - return CheckIntegerFloatTruncateImpl<UInt64Type, FloatType>(input); - } else { - return CheckIntegerFloatTruncateImpl<UInt64Type, DoubleType>(input); - } - } - default: - break; - } - DCHECK(false); - return Status::OK(); -} - +} + Status CastIntegerToFloating(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; - Type::type out_type = out->type()->id(); - if (!options.allow_float_truncate) { + const auto& options = checked_cast<const CastState*>(ctx->state())->options; + Type::type out_type = out->type()->id(); + if (!options.allow_float_truncate) { RETURN_NOT_OK(CheckForIntegerToFloatingTruncation(batch[0], out_type)); - } - CastNumberToNumberUnsafe(batch[0].type()->id(), out_type, batch[0], out); + } + CastNumberToNumberUnsafe(batch[0].type()->id(), out_type, batch[0], out); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Boolean to number - -struct BooleanToNumber { - template <typename OutValue, typename Arg0Value> +} + +// ---------------------------------------------------------------------- +// Boolean to number + +struct BooleanToNumber { + template <typename OutValue, typename Arg0Value> static OutValue Call(KernelContext*, Arg0Value val, Status*) { - constexpr auto kOne = static_cast<OutValue>(1); - constexpr auto kZero = static_cast<OutValue>(0); - return val ? kOne : kZero; - } -}; - -template <typename O> -struct CastFunctor<O, BooleanType, enable_if_number<O>> { + constexpr auto kOne = static_cast<OutValue>(1); + constexpr auto kZero = static_cast<OutValue>(0); + return val ? kOne : kZero; + } +}; + +template <typename O> +struct CastFunctor<O, BooleanType, enable_if_number<O>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return applicator::ScalarUnary<O, BooleanType, BooleanToNumber>::Exec(ctx, batch, out); - } -}; - -// ---------------------------------------------------------------------- -// String to number - -template <typename OutType> -struct ParseString { - template <typename OutValue, typename Arg0Value> + } +}; + +// ---------------------------------------------------------------------- +// String to number + +template <typename OutType> +struct ParseString { + template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) const { - OutValue result = OutValue(0); - if (ARROW_PREDICT_FALSE(!ParseValue<OutType>(val.data(), val.size(), &result))) { + OutValue result = OutValue(0); + if (ARROW_PREDICT_FALSE(!ParseValue<OutType>(val.data(), val.size(), &result))) { *st = Status::Invalid("Failed to parse string: '", val, "' as a scalar of type ", TypeTraits<OutType>::type_singleton()->ToString()); - } - return result; - } -}; - -template <typename O, typename I> -struct CastFunctor<O, I, enable_if_base_binary<I>> { + } + return result; + } +}; + +template <typename O, typename I> +struct CastFunctor<O, I, enable_if_base_binary<I>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return applicator::ScalarUnaryNotNull<O, I, ParseString<O>>::Exec(ctx, batch, out); - } -}; - -// ---------------------------------------------------------------------- -// Decimal to integer - -struct DecimalToIntegerMixin { + } +}; + +// ---------------------------------------------------------------------- +// Decimal to integer + +struct DecimalToIntegerMixin { template <typename OutValue, typename Arg0Value> OutValue ToInteger(KernelContext* ctx, const Arg0Value& val, Status* st) const { - constexpr auto min_value = std::numeric_limits<OutValue>::min(); - constexpr auto max_value = std::numeric_limits<OutValue>::max(); - - if (!allow_int_overflow_ && ARROW_PREDICT_FALSE(val < min_value || val > max_value)) { + constexpr auto min_value = std::numeric_limits<OutValue>::min(); + constexpr auto max_value = std::numeric_limits<OutValue>::max(); + + if (!allow_int_overflow_ && ARROW_PREDICT_FALSE(val < min_value || val > max_value)) { *st = Status::Invalid("Integer value out of bounds"); - return OutValue{}; // Zero - } else { - return static_cast<OutValue>(val.low_bits()); - } - } - - DecimalToIntegerMixin(int32_t in_scale, bool allow_int_overflow) - : in_scale_(in_scale), allow_int_overflow_(allow_int_overflow) {} - - int32_t in_scale_; - bool allow_int_overflow_; -}; - -struct UnsafeUpscaleDecimalToInteger : public DecimalToIntegerMixin { - using DecimalToIntegerMixin::DecimalToIntegerMixin; - - template <typename OutValue, typename Arg0Value> + return OutValue{}; // Zero + } else { + return static_cast<OutValue>(val.low_bits()); + } + } + + DecimalToIntegerMixin(int32_t in_scale, bool allow_int_overflow) + : in_scale_(in_scale), allow_int_overflow_(allow_int_overflow) {} + + int32_t in_scale_; + bool allow_int_overflow_; +}; + +struct UnsafeUpscaleDecimalToInteger : public DecimalToIntegerMixin { + using DecimalToIntegerMixin::DecimalToIntegerMixin; + + template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) const { return ToInteger<OutValue>(ctx, val.IncreaseScaleBy(-in_scale_), st); - } -}; - -struct UnsafeDownscaleDecimalToInteger : public DecimalToIntegerMixin { - using DecimalToIntegerMixin::DecimalToIntegerMixin; - - template <typename OutValue, typename Arg0Value> + } +}; + +struct UnsafeDownscaleDecimalToInteger : public DecimalToIntegerMixin { + using DecimalToIntegerMixin::DecimalToIntegerMixin; + + template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) const { return ToInteger<OutValue>(ctx, val.ReduceScaleBy(in_scale_, false), st); - } -}; - -struct SafeRescaleDecimalToInteger : public DecimalToIntegerMixin { - using DecimalToIntegerMixin::DecimalToIntegerMixin; - - template <typename OutValue, typename Arg0Value> + } +}; + +struct SafeRescaleDecimalToInteger : public DecimalToIntegerMixin { + using DecimalToIntegerMixin::DecimalToIntegerMixin; + + template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext* ctx, Arg0Value val, Status* st) const { - auto result = val.Rescale(in_scale_, 0); - if (ARROW_PREDICT_FALSE(!result.ok())) { + auto result = val.Rescale(in_scale_, 0); + if (ARROW_PREDICT_FALSE(!result.ok())) { *st = result.status(); - return OutValue{}; // Zero - } else { + return OutValue{}; // Zero + } else { return ToInteger<OutValue>(ctx, *result, st); - } - } -}; - + } + } +}; + template <typename O, typename I> struct CastFunctor<O, I, enable_if_t<is_integer_type<O>::value && is_decimal_type<I>::value>> { - using out_type = typename O::c_type; - + using out_type = typename O::c_type; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; - + const auto& options = checked_cast<const CastState*>(ctx->state())->options; + const auto& in_type_inst = checked_cast<const I&>(*batch[0].type()); - const auto in_scale = in_type_inst.scale(); - - if (options.allow_decimal_truncate) { - if (in_scale < 0) { - // Unsafe upscale + const auto in_scale = in_type_inst.scale(); + + if (options.allow_decimal_truncate) { + if (in_scale < 0) { + // Unsafe upscale applicator::ScalarUnaryNotNullStateful<O, I, UnsafeUpscaleDecimalToInteger> - kernel(UnsafeUpscaleDecimalToInteger{in_scale, options.allow_int_overflow}); - return kernel.Exec(ctx, batch, out); - } else { - // Unsafe downscale + kernel(UnsafeUpscaleDecimalToInteger{in_scale, options.allow_int_overflow}); + return kernel.Exec(ctx, batch, out); + } else { + // Unsafe downscale applicator::ScalarUnaryNotNullStateful<O, I, UnsafeDownscaleDecimalToInteger> - kernel(UnsafeDownscaleDecimalToInteger{in_scale, options.allow_int_overflow}); - return kernel.Exec(ctx, batch, out); - } - } else { - // Safe rescale + kernel(UnsafeDownscaleDecimalToInteger{in_scale, options.allow_int_overflow}); + return kernel.Exec(ctx, batch, out); + } + } else { + // Safe rescale applicator::ScalarUnaryNotNullStateful<O, I, SafeRescaleDecimalToInteger> kernel( SafeRescaleDecimalToInteger{in_scale, options.allow_int_overflow}); - return kernel.Exec(ctx, batch, out); - } - } -}; - -// ---------------------------------------------------------------------- -// Decimal to decimal - + return kernel.Exec(ctx, batch, out); + } + } +}; + +// ---------------------------------------------------------------------- +// Decimal to decimal + // Helper that converts the input and output decimals // For instance, Decimal128 -> Decimal256 requires converting, then scaling // Decimal256 -> Decimal128 requires scaling, then truncating @@ -413,15 +413,15 @@ struct DecimalConversions<Decimal128, Decimal256> { static Decimal256 ConvertInput(Decimal256&& val) { return val; } static Decimal128 ConvertOutput(Decimal256&& val) { return Decimal128(val.little_endian_array()[1], val.little_endian_array()[0]); - } + } }; - + template <> struct DecimalConversions<Decimal128, Decimal128> { static Decimal128 ConvertInput(Decimal128&& val) { return val; } static Decimal128 ConvertOutput(Decimal128&& val) { return val; } -}; - +}; + struct UnsafeUpscaleDecimal { template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext*, Arg0Value val, Status*) const { @@ -431,17 +431,17 @@ struct UnsafeUpscaleDecimal { int32_t by_; }; -struct UnsafeDownscaleDecimal { +struct UnsafeDownscaleDecimal { template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext*, Arg0Value val, Status*) const { using Conv = DecimalConversions<OutValue, Arg0Value>; return Conv::ConvertOutput( Conv::ConvertInput(std::move(val)).ReduceScaleBy(by_, false)); - } + } int32_t by_; -}; - -struct SafeRescaleDecimal { +}; + +struct SafeRescaleDecimal { template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext*, Arg0Value val, Status* st) const { using Conv = DecimalConversions<OutValue, Arg0Value>; @@ -450,7 +450,7 @@ struct SafeRescaleDecimal { if (ARROW_PREDICT_FALSE(!maybe_rescaled.ok())) { *st = maybe_rescaled.status(); return {}; // Zero - } + } if (ARROW_PREDICT_TRUE(maybe_rescaled->FitsInPrecision(out_precision_))) { return Conv::ConvertOutput(maybe_rescaled.MoveValueUnsafe()); @@ -458,199 +458,199 @@ struct SafeRescaleDecimal { *st = Status::Invalid("Decimal value does not fit in precision ", out_precision_); return {}; // Zero - } - - int32_t out_scale_, out_precision_, in_scale_; -}; - + } + + int32_t out_scale_, out_precision_, in_scale_; +}; + template <typename O, typename I> struct CastFunctor<O, I, enable_if_t<is_decimal_type<O>::value && is_decimal_type<I>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; - + const auto& options = checked_cast<const CastState*>(ctx->state())->options; + const auto& in_type = checked_cast<const I&>(*batch[0].type()); const auto& out_type = checked_cast<const O&>(*out->type()); const auto in_scale = in_type.scale(); const auto out_scale = out_type.scale(); - - if (options.allow_decimal_truncate) { - if (in_scale < out_scale) { - // Unsafe upscale + + if (options.allow_decimal_truncate) { + if (in_scale < out_scale) { + // Unsafe upscale applicator::ScalarUnaryNotNullStateful<O, I, UnsafeUpscaleDecimal> kernel( UnsafeUpscaleDecimal{out_scale - in_scale}); - return kernel.Exec(ctx, batch, out); - } else { - // Unsafe downscale + return kernel.Exec(ctx, batch, out); + } else { + // Unsafe downscale applicator::ScalarUnaryNotNullStateful<O, I, UnsafeDownscaleDecimal> kernel( UnsafeDownscaleDecimal{in_scale - out_scale}); - return kernel.Exec(ctx, batch, out); - } - } + return kernel.Exec(ctx, batch, out); + } + } // Safe rescale applicator::ScalarUnaryNotNullStateful<O, I, SafeRescaleDecimal> kernel( SafeRescaleDecimal{out_scale, out_type.precision(), in_scale}); return kernel.Exec(ctx, batch, out); - } -}; - -// ---------------------------------------------------------------------- -// Real to decimal - -struct RealToDecimal { - template <typename OutValue, typename RealType> + } +}; + +// ---------------------------------------------------------------------- +// Real to decimal + +struct RealToDecimal { + template <typename OutValue, typename RealType> OutValue Call(KernelContext*, RealType val, Status* st) const { auto maybe_decimal = OutValue::FromReal(val, out_precision_, out_scale_); if (ARROW_PREDICT_TRUE(maybe_decimal.ok())) { return maybe_decimal.MoveValueUnsafe(); - } + } if (!allow_truncate_) { *st = maybe_decimal.status(); } return {}; // Zero - } - - int32_t out_scale_, out_precision_; - bool allow_truncate_; -}; - + } + + int32_t out_scale_, out_precision_; + bool allow_truncate_; +}; + template <typename O, typename I> struct CastFunctor<O, I, enable_if_t<is_decimal_type<O>::value && is_floating_type<I>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& options = checked_cast<const CastState*>(ctx->state())->options; + const auto& options = checked_cast<const CastState*>(ctx->state())->options; const auto& out_type = checked_cast<const O&>(*out->type()); const auto out_scale = out_type.scale(); const auto out_precision = out_type.precision(); - + applicator::ScalarUnaryNotNullStateful<O, I, RealToDecimal> kernel( - RealToDecimal{out_scale, out_precision, options.allow_decimal_truncate}); - return kernel.Exec(ctx, batch, out); - } -}; - -// ---------------------------------------------------------------------- -// Decimal to real - -struct DecimalToReal { - template <typename RealType, typename Arg0Value> + RealToDecimal{out_scale, out_precision, options.allow_decimal_truncate}); + return kernel.Exec(ctx, batch, out); + } +}; + +// ---------------------------------------------------------------------- +// Decimal to real + +struct DecimalToReal { + template <typename RealType, typename Arg0Value> RealType Call(KernelContext*, const Arg0Value& val, Status*) const { return val.template ToReal<RealType>(in_scale_); - } - - int32_t in_scale_; -}; - + } + + int32_t in_scale_; +}; + template <typename O, typename I> struct CastFunctor<O, I, enable_if_t<is_floating_type<O>::value && is_decimal_type<I>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { const auto& in_type = checked_cast<const I&>(*batch[0].type()); const auto in_scale = in_type.scale(); - + applicator::ScalarUnaryNotNullStateful<O, I, DecimalToReal> kernel( - DecimalToReal{in_scale}); - return kernel.Exec(ctx, batch, out); - } -}; - -// ---------------------------------------------------------------------- -// Top-level kernel instantiation - -namespace { - -template <typename OutType> -void AddCommonNumberCasts(const std::shared_ptr<DataType>& out_ty, CastFunction* func) { - AddCommonCasts(out_ty->id(), out_ty, func); - - // Cast from boolean to number - DCHECK_OK(func->AddKernel(Type::BOOL, {boolean()}, out_ty, - CastFunctor<OutType, BooleanType>::Exec)); - - // Cast from other strings - for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) { - auto exec = GenerateVarBinaryBase<CastFunctor, OutType>(*in_ty); - DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, exec)); - } -} - -template <typename OutType> -std::shared_ptr<CastFunction> GetCastToInteger(std::string name) { - auto func = std::make_shared<CastFunction>(std::move(name), OutType::type_id); - auto out_ty = TypeTraits<OutType>::type_singleton(); - - for (const std::shared_ptr<DataType>& in_ty : IntTypes()) { - DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastIntegerToInteger)); - } - - // Cast from floating point - for (const std::shared_ptr<DataType>& in_ty : FloatingPointTypes()) { - DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastFloatingToInteger)); - } - - // From other numbers to integer - AddCommonNumberCasts<OutType>(out_ty, func.get()); - - // From decimal to integer + DecimalToReal{in_scale}); + return kernel.Exec(ctx, batch, out); + } +}; + +// ---------------------------------------------------------------------- +// Top-level kernel instantiation + +namespace { + +template <typename OutType> +void AddCommonNumberCasts(const std::shared_ptr<DataType>& out_ty, CastFunction* func) { + AddCommonCasts(out_ty->id(), out_ty, func); + + // Cast from boolean to number + DCHECK_OK(func->AddKernel(Type::BOOL, {boolean()}, out_ty, + CastFunctor<OutType, BooleanType>::Exec)); + + // Cast from other strings + for (const std::shared_ptr<DataType>& in_ty : BaseBinaryTypes()) { + auto exec = GenerateVarBinaryBase<CastFunctor, OutType>(*in_ty); + DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, exec)); + } +} + +template <typename OutType> +std::shared_ptr<CastFunction> GetCastToInteger(std::string name) { + auto func = std::make_shared<CastFunction>(std::move(name), OutType::type_id); + auto out_ty = TypeTraits<OutType>::type_singleton(); + + for (const std::shared_ptr<DataType>& in_ty : IntTypes()) { + DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastIntegerToInteger)); + } + + // Cast from floating point + for (const std::shared_ptr<DataType>& in_ty : FloatingPointTypes()) { + DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastFloatingToInteger)); + } + + // From other numbers to integer + AddCommonNumberCasts<OutType>(out_ty, func.get()); + + // From decimal to integer DCHECK_OK(func->AddKernel(Type::DECIMAL, {InputType(Type::DECIMAL)}, out_ty, - CastFunctor<OutType, Decimal128Type>::Exec)); + CastFunctor<OutType, Decimal128Type>::Exec)); DCHECK_OK(func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, out_ty, CastFunctor<OutType, Decimal256Type>::Exec)); - return func; -} - -template <typename OutType> -std::shared_ptr<CastFunction> GetCastToFloating(std::string name) { - auto func = std::make_shared<CastFunction>(std::move(name), OutType::type_id); - auto out_ty = TypeTraits<OutType>::type_singleton(); - - // Casts from integer to floating point - for (const std::shared_ptr<DataType>& in_ty : IntTypes()) { - DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastIntegerToFloating)); - } - - // Cast from floating point - for (const std::shared_ptr<DataType>& in_ty : FloatingPointTypes()) { - DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastFloatingToFloating)); - } - - // From other numbers to floating point - AddCommonNumberCasts<OutType>(out_ty, func.get()); - - // From decimal to floating point + return func; +} + +template <typename OutType> +std::shared_ptr<CastFunction> GetCastToFloating(std::string name) { + auto func = std::make_shared<CastFunction>(std::move(name), OutType::type_id); + auto out_ty = TypeTraits<OutType>::type_singleton(); + + // Casts from integer to floating point + for (const std::shared_ptr<DataType>& in_ty : IntTypes()) { + DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastIntegerToFloating)); + } + + // Cast from floating point + for (const std::shared_ptr<DataType>& in_ty : FloatingPointTypes()) { + DCHECK_OK(func->AddKernel(in_ty->id(), {in_ty}, out_ty, CastFloatingToFloating)); + } + + // From other numbers to floating point + AddCommonNumberCasts<OutType>(out_ty, func.get()); + + // From decimal to floating point DCHECK_OK(func->AddKernel(Type::DECIMAL, {InputType(Type::DECIMAL)}, out_ty, - CastFunctor<OutType, Decimal128Type>::Exec)); + CastFunctor<OutType, Decimal128Type>::Exec)); DCHECK_OK(func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, out_ty, CastFunctor<OutType, Decimal256Type>::Exec)); - return func; -} - + return func; +} + std::shared_ptr<CastFunction> GetCastToDecimal128() { - OutputType sig_out_ty(ResolveOutputFromOptions); - + OutputType sig_out_ty(ResolveOutputFromOptions); + auto func = std::make_shared<CastFunction>("cast_decimal", Type::DECIMAL128); AddCommonCasts(Type::DECIMAL128, sig_out_ty, func.get()); - - // Cast from floating point - DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty, - CastFunctor<Decimal128Type, FloatType>::Exec)); - DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty, - CastFunctor<Decimal128Type, DoubleType>::Exec)); - - // Cast from other decimal - auto exec = CastFunctor<Decimal128Type, Decimal128Type>::Exec; - // We resolve the output type of this kernel from the CastOptions + + // Cast from floating point + DCHECK_OK(func->AddKernel(Type::FLOAT, {float32()}, sig_out_ty, + CastFunctor<Decimal128Type, FloatType>::Exec)); + DCHECK_OK(func->AddKernel(Type::DOUBLE, {float64()}, sig_out_ty, + CastFunctor<Decimal128Type, DoubleType>::Exec)); + + // Cast from other decimal + auto exec = CastFunctor<Decimal128Type, Decimal128Type>::Exec; + // We resolve the output type of this kernel from the CastOptions DCHECK_OK( func->AddKernel(Type::DECIMAL128, {InputType(Type::DECIMAL128)}, sig_out_ty, exec)); exec = CastFunctor<Decimal128Type, Decimal256Type>::Exec; DCHECK_OK( func->AddKernel(Type::DECIMAL256, {InputType(Type::DECIMAL256)}, sig_out_ty, exec)); - return func; -} - + return func; +} + std::shared_ptr<CastFunction> GetCastToDecimal256() { OutputType sig_out_ty(ResolveOutputFromOptions); @@ -673,55 +673,55 @@ std::shared_ptr<CastFunction> GetCastToDecimal256() { return func; } -} // namespace - -std::vector<std::shared_ptr<CastFunction>> GetNumericCasts() { - std::vector<std::shared_ptr<CastFunction>> functions; - - // Make a cast to null that does not do much. Not sure why we need to be able - // to cast from dict<null> -> null but there are unit tests for it - auto cast_null = std::make_shared<CastFunction>("cast_null", Type::NA); +} // namespace + +std::vector<std::shared_ptr<CastFunction>> GetNumericCasts() { + std::vector<std::shared_ptr<CastFunction>> functions; + + // Make a cast to null that does not do much. Not sure why we need to be able + // to cast from dict<null> -> null but there are unit tests for it + auto cast_null = std::make_shared<CastFunction>("cast_null", Type::NA); DCHECK_OK(cast_null->AddKernel(Type::DICTIONARY, {InputType(Type::DICTIONARY)}, null(), OutputAllNull)); - functions.push_back(cast_null); - - functions.push_back(GetCastToInteger<Int8Type>("cast_int8")); - functions.push_back(GetCastToInteger<Int16Type>("cast_int16")); - - auto cast_int32 = GetCastToInteger<Int32Type>("cast_int32"); - // Convert DATE32 or TIME32 to INT32 zero copy - AddZeroCopyCast(Type::DATE32, date32(), int32(), cast_int32.get()); - AddZeroCopyCast(Type::TIME32, InputType(Type::TIME32), int32(), cast_int32.get()); - functions.push_back(cast_int32); - - auto cast_int64 = GetCastToInteger<Int64Type>("cast_int64"); - // Convert DATE64, DURATION, TIMESTAMP, TIME64 to INT64 zero copy - AddZeroCopyCast(Type::DATE64, InputType(Type::DATE64), int64(), cast_int64.get()); - AddZeroCopyCast(Type::DURATION, InputType(Type::DURATION), int64(), cast_int64.get()); - AddZeroCopyCast(Type::TIMESTAMP, InputType(Type::TIMESTAMP), int64(), cast_int64.get()); - AddZeroCopyCast(Type::TIME64, InputType(Type::TIME64), int64(), cast_int64.get()); - functions.push_back(cast_int64); - - functions.push_back(GetCastToInteger<UInt8Type>("cast_uint8")); - functions.push_back(GetCastToInteger<UInt16Type>("cast_uint16")); - functions.push_back(GetCastToInteger<UInt32Type>("cast_uint32")); - functions.push_back(GetCastToInteger<UInt64Type>("cast_uint64")); - - // HalfFloat is a bit brain-damaged for now - auto cast_half_float = - std::make_shared<CastFunction>("cast_half_float", Type::HALF_FLOAT); - AddCommonCasts(Type::HALF_FLOAT, float16(), cast_half_float.get()); - functions.push_back(cast_half_float); - - functions.push_back(GetCastToFloating<FloatType>("cast_float")); - functions.push_back(GetCastToFloating<DoubleType>("cast_double")); - + functions.push_back(cast_null); + + functions.push_back(GetCastToInteger<Int8Type>("cast_int8")); + functions.push_back(GetCastToInteger<Int16Type>("cast_int16")); + + auto cast_int32 = GetCastToInteger<Int32Type>("cast_int32"); + // Convert DATE32 or TIME32 to INT32 zero copy + AddZeroCopyCast(Type::DATE32, date32(), int32(), cast_int32.get()); + AddZeroCopyCast(Type::TIME32, InputType(Type::TIME32), int32(), cast_int32.get()); + functions.push_back(cast_int32); + + auto cast_int64 = GetCastToInteger<Int64Type>("cast_int64"); + // Convert DATE64, DURATION, TIMESTAMP, TIME64 to INT64 zero copy + AddZeroCopyCast(Type::DATE64, InputType(Type::DATE64), int64(), cast_int64.get()); + AddZeroCopyCast(Type::DURATION, InputType(Type::DURATION), int64(), cast_int64.get()); + AddZeroCopyCast(Type::TIMESTAMP, InputType(Type::TIMESTAMP), int64(), cast_int64.get()); + AddZeroCopyCast(Type::TIME64, InputType(Type::TIME64), int64(), cast_int64.get()); + functions.push_back(cast_int64); + + functions.push_back(GetCastToInteger<UInt8Type>("cast_uint8")); + functions.push_back(GetCastToInteger<UInt16Type>("cast_uint16")); + functions.push_back(GetCastToInteger<UInt32Type>("cast_uint32")); + functions.push_back(GetCastToInteger<UInt64Type>("cast_uint64")); + + // HalfFloat is a bit brain-damaged for now + auto cast_half_float = + std::make_shared<CastFunction>("cast_half_float", Type::HALF_FLOAT); + AddCommonCasts(Type::HALF_FLOAT, float16(), cast_half_float.get()); + functions.push_back(cast_half_float); + + functions.push_back(GetCastToFloating<FloatType>("cast_float")); + functions.push_back(GetCastToFloating<DoubleType>("cast_double")); + functions.push_back(GetCastToDecimal128()); functions.push_back(GetCastToDecimal256()); - - return functions; -} - -} // namespace internal -} // namespace compute -} // namespace arrow + + return functions; +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_string.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_string.cc index 56a19a69a1..3ce537b722 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_string.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_string.cc @@ -1,107 +1,107 @@ -// 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. - +// 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. + #include <limits> - -#include "arrow/array/array_base.h" + +#include "arrow/array/array_base.h" #include "arrow/array/builder_binary.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/scalar_cast_internal.h" -#include "arrow/result.h" -#include "arrow/util/formatting.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/result.h" +#include "arrow/util/formatting.h" #include "arrow/util/int_util.h" -#include "arrow/util/optional.h" -#include "arrow/util/utf8.h" -#include "arrow/visitor_inline.h" - -namespace arrow { - -using internal::StringFormatter; -using util::InitializeUTF8; -using util::ValidateUTF8; - -namespace compute { -namespace internal { - +#include "arrow/util/optional.h" +#include "arrow/util/utf8.h" +#include "arrow/visitor_inline.h" + +namespace arrow { + +using internal::StringFormatter; +using util::InitializeUTF8; +using util::ValidateUTF8; + +namespace compute { +namespace internal { + namespace { -// ---------------------------------------------------------------------- -// Number / Boolean to String - +// ---------------------------------------------------------------------- +// Number / Boolean to String + template <typename O, typename I> struct NumericToStringCastFunctor { - using value_type = typename TypeTraits<I>::CType; - using BuilderType = typename TypeTraits<O>::BuilderType; - using FormatterType = StringFormatter<I>; - + using value_type = typename TypeTraits<I>::CType; + using BuilderType = typename TypeTraits<O>::BuilderType; + using FormatterType = StringFormatter<I>; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { DCHECK(out->is_array()); - const ArrayData& input = *batch[0].array(); - ArrayData* output = out->mutable_array(); + const ArrayData& input = *batch[0].array(); + ArrayData* output = out->mutable_array(); return Convert(ctx, input, output); - } - - static Status Convert(KernelContext* ctx, const ArrayData& input, ArrayData* output) { - FormatterType formatter(input.type); - BuilderType builder(input.type, ctx->memory_pool()); - RETURN_NOT_OK(VisitArrayDataInline<I>( - input, - [&](value_type v) { - return formatter(v, [&](util::string_view v) { return builder.Append(v); }); - }, - [&]() { return builder.AppendNull(); })); - - std::shared_ptr<Array> output_array; - RETURN_NOT_OK(builder.Finish(&output_array)); - *output = std::move(*output_array->data()); - return Status::OK(); - } -}; - -// ---------------------------------------------------------------------- + } + + static Status Convert(KernelContext* ctx, const ArrayData& input, ArrayData* output) { + FormatterType formatter(input.type); + BuilderType builder(input.type, ctx->memory_pool()); + RETURN_NOT_OK(VisitArrayDataInline<I>( + input, + [&](value_type v) { + return formatter(v, [&](util::string_view v) { return builder.Append(v); }); + }, + [&]() { return builder.AppendNull(); })); + + std::shared_ptr<Array> output_array; + RETURN_NOT_OK(builder.Finish(&output_array)); + *output = std::move(*output_array->data()); + return Status::OK(); + } +}; + +// ---------------------------------------------------------------------- // Binary-like to binary-like -// - -#if defined(_MSC_VER) -// Silence warning: """'visitor': unreferenced local variable""" -#pragma warning(push) -#pragma warning(disable : 4101) -#endif - -struct Utf8Validator { - Status VisitNull() { return Status::OK(); } - - Status VisitValue(util::string_view str) { - if (ARROW_PREDICT_FALSE(!ValidateUTF8(str))) { - return Status::Invalid("Invalid UTF8 payload"); - } - return Status::OK(); - } -}; - -template <typename I, typename O> +// + +#if defined(_MSC_VER) +// Silence warning: """'visitor': unreferenced local variable""" +#pragma warning(push) +#pragma warning(disable : 4101) +#endif + +struct Utf8Validator { + Status VisitNull() { return Status::OK(); } + + Status VisitValue(util::string_view str) { + if (ARROW_PREDICT_FALSE(!ValidateUTF8(str))) { + return Status::Invalid("Invalid UTF8 payload"); + } + return Status::OK(); + } +}; + +template <typename I, typename O> Status CastBinaryToBinaryOffsets(KernelContext* ctx, const ArrayData& input, ArrayData* output) { static_assert(std::is_same<I, O>::value, "Cast same-width offsets (no-op)"); return Status::OK(); } - + // Upcast offsets -template <> +template <> Status CastBinaryToBinaryOffsets<int32_t, int64_t>(KernelContext* ctx, const ArrayData& input, ArrayData* output) { @@ -117,15 +117,15 @@ Status CastBinaryToBinaryOffsets<int32_t, int64_t>(KernelContext* ctx, output->length + 1); return Status::OK(); } - + // Downcast offsets -template <> +template <> Status CastBinaryToBinaryOffsets<int64_t, int32_t>(KernelContext* ctx, const ArrayData& input, ArrayData* output) { using input_offset_type = int64_t; using output_offset_type = int32_t; - + constexpr input_offset_type kMaxOffset = std::numeric_limits<output_offset_type>::max(); auto input_offsets = input.GetValues<input_offset_type>(1); @@ -167,31 +167,31 @@ Status BinaryToBinaryCastExec(KernelContext* ctx, const ExecBatch& batch, Datum* ctx, input, out->mutable_array()); } -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + // ---------------------------------------------------------------------- // Cast functions registration - -template <typename OutType> + +template <typename OutType> void AddNumberToStringCasts(CastFunction* func) { auto out_ty = TypeTraits<OutType>::type_singleton(); - DCHECK_OK(func->AddKernel(Type::BOOL, {boolean()}, out_ty, + DCHECK_OK(func->AddKernel(Type::BOOL, {boolean()}, out_ty, TrivialScalarUnaryAsArraysExec( NumericToStringCastFunctor<OutType, BooleanType>::Exec), - NullHandling::COMPUTED_NO_PREALLOCATE)); - - for (const std::shared_ptr<DataType>& in_ty : NumericTypes()) { + NullHandling::COMPUTED_NO_PREALLOCATE)); + + for (const std::shared_ptr<DataType>& in_ty : NumericTypes()) { DCHECK_OK( func->AddKernel(in_ty->id(), {in_ty}, out_ty, TrivialScalarUnaryAsArraysExec( GenerateNumeric<NumericToStringCastFunctor, OutType>(*in_ty)), NullHandling::COMPUTED_NO_PREALLOCATE)); - } -} - + } +} + template <typename OutType, typename InType> void AddBinaryToBinaryCast(CastFunction* func) { auto in_ty = TypeTraits<InType>::type_singleton(); @@ -213,35 +213,35 @@ void AddBinaryToBinaryCast(CastFunction* func) { } // namespace -std::vector<std::shared_ptr<CastFunction>> GetBinaryLikeCasts() { - auto cast_binary = std::make_shared<CastFunction>("cast_binary", Type::BINARY); - AddCommonCasts(Type::BINARY, binary(), cast_binary.get()); +std::vector<std::shared_ptr<CastFunction>> GetBinaryLikeCasts() { + auto cast_binary = std::make_shared<CastFunction>("cast_binary", Type::BINARY); + AddCommonCasts(Type::BINARY, binary(), cast_binary.get()); AddBinaryToBinaryCast<BinaryType>(cast_binary.get()); - - auto cast_large_binary = - std::make_shared<CastFunction>("cast_large_binary", Type::LARGE_BINARY); - AddCommonCasts(Type::LARGE_BINARY, large_binary(), cast_large_binary.get()); + + auto cast_large_binary = + std::make_shared<CastFunction>("cast_large_binary", Type::LARGE_BINARY); + AddCommonCasts(Type::LARGE_BINARY, large_binary(), cast_large_binary.get()); AddBinaryToBinaryCast<LargeBinaryType>(cast_large_binary.get()); - - auto cast_string = std::make_shared<CastFunction>("cast_string", Type::STRING); - AddCommonCasts(Type::STRING, utf8(), cast_string.get()); + + auto cast_string = std::make_shared<CastFunction>("cast_string", Type::STRING); + AddCommonCasts(Type::STRING, utf8(), cast_string.get()); AddNumberToStringCasts<StringType>(cast_string.get()); AddBinaryToBinaryCast<StringType>(cast_string.get()); - - auto cast_large_string = - std::make_shared<CastFunction>("cast_large_string", Type::LARGE_STRING); - AddCommonCasts(Type::LARGE_STRING, large_utf8(), cast_large_string.get()); + + auto cast_large_string = + std::make_shared<CastFunction>("cast_large_string", Type::LARGE_STRING); + AddCommonCasts(Type::LARGE_STRING, large_utf8(), cast_large_string.get()); AddNumberToStringCasts<LargeStringType>(cast_large_string.get()); AddBinaryToBinaryCast<LargeStringType>(cast_large_string.get()); - + auto cast_fsb = std::make_shared<CastFunction>("cast_fixed_size_binary", Type::FIXED_SIZE_BINARY); AddCommonCasts(Type::FIXED_SIZE_BINARY, OutputType(ResolveOutputFromOptions), cast_fsb.get()); return {cast_binary, cast_large_binary, cast_string, cast_large_string, cast_fsb}; -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_temporal.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_temporal.cc index a06d473329..1a58fce7c7 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_temporal.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_cast_temporal.cc @@ -1,260 +1,260 @@ -// 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. - -// Implementation of casting to (or between) temporal types - -#include <limits> - +// 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. + +// Implementation of casting to (or between) temporal types + +#include <limits> + #include "arrow/array/builder_time.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/scalar_cast_internal.h" -#include "arrow/util/bitmap_reader.h" -#include "arrow/util/time.h" -#include "arrow/util/value_parsing.h" - -namespace arrow { - -using internal::ParseValue; - -namespace compute { -namespace internal { - -constexpr int64_t kMillisecondsInDay = 86400000; - -// ---------------------------------------------------------------------- -// From one timestamp to another - -template <typename in_type, typename out_type> +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/scalar_cast_internal.h" +#include "arrow/util/bitmap_reader.h" +#include "arrow/util/time.h" +#include "arrow/util/value_parsing.h" + +namespace arrow { + +using internal::ParseValue; + +namespace compute { +namespace internal { + +constexpr int64_t kMillisecondsInDay = 86400000; + +// ---------------------------------------------------------------------- +// From one timestamp to another + +template <typename in_type, typename out_type> Status ShiftTime(KernelContext* ctx, const util::DivideOrMultiply factor_op, const int64_t factor, const ArrayData& input, ArrayData* output) { - const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; + const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; auto in_data = input.GetValues<in_type>(1); - auto out_data = output->GetMutableValues<out_type>(1); - - if (factor == 1) { - for (int64_t i = 0; i < input.length; i++) { - out_data[i] = static_cast<out_type>(in_data[i]); - } - } else if (factor_op == util::MULTIPLY) { - if (options.allow_time_overflow) { - for (int64_t i = 0; i < input.length; i++) { - out_data[i] = static_cast<out_type>(in_data[i] * factor); - } - } else { + auto out_data = output->GetMutableValues<out_type>(1); + + if (factor == 1) { + for (int64_t i = 0; i < input.length; i++) { + out_data[i] = static_cast<out_type>(in_data[i]); + } + } else if (factor_op == util::MULTIPLY) { + if (options.allow_time_overflow) { + for (int64_t i = 0; i < input.length; i++) { + out_data[i] = static_cast<out_type>(in_data[i] * factor); + } + } else { #define RAISE_OVERFLOW_CAST(VAL) \ return Status::Invalid("Casting from ", input.type->ToString(), " to ", \ output->type->ToString(), " would result in ", \ "out of bounds timestamp: ", VAL); - - int64_t max_val = std::numeric_limits<int64_t>::max() / factor; - int64_t min_val = std::numeric_limits<int64_t>::min() / factor; - if (input.null_count != 0) { - BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); - for (int64_t i = 0; i < input.length; i++) { - if (bit_reader.IsSet() && (in_data[i] < min_val || in_data[i] > max_val)) { - RAISE_OVERFLOW_CAST(in_data[i]); - } - out_data[i] = static_cast<out_type>(in_data[i] * factor); - bit_reader.Next(); - } - } else { - for (int64_t i = 0; i < input.length; i++) { - if (in_data[i] < min_val || in_data[i] > max_val) { - RAISE_OVERFLOW_CAST(in_data[i]); - } - out_data[i] = static_cast<out_type>(in_data[i] * factor); - } - } - -#undef RAISE_OVERFLOW_CAST - } - } else { - if (options.allow_time_truncate) { - for (int64_t i = 0; i < input.length; i++) { - out_data[i] = static_cast<out_type>(in_data[i] / factor); - } - } else { + + int64_t max_val = std::numeric_limits<int64_t>::max() / factor; + int64_t min_val = std::numeric_limits<int64_t>::min() / factor; + if (input.null_count != 0) { + BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); + for (int64_t i = 0; i < input.length; i++) { + if (bit_reader.IsSet() && (in_data[i] < min_val || in_data[i] > max_val)) { + RAISE_OVERFLOW_CAST(in_data[i]); + } + out_data[i] = static_cast<out_type>(in_data[i] * factor); + bit_reader.Next(); + } + } else { + for (int64_t i = 0; i < input.length; i++) { + if (in_data[i] < min_val || in_data[i] > max_val) { + RAISE_OVERFLOW_CAST(in_data[i]); + } + out_data[i] = static_cast<out_type>(in_data[i] * factor); + } + } + +#undef RAISE_OVERFLOW_CAST + } + } else { + if (options.allow_time_truncate) { + for (int64_t i = 0; i < input.length; i++) { + out_data[i] = static_cast<out_type>(in_data[i] / factor); + } + } else { #define RAISE_INVALID_CAST(VAL) \ return Status::Invalid("Casting from ", input.type->ToString(), " to ", \ output->type->ToString(), " would lose data: ", VAL); - - if (input.null_count != 0) { - BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); - for (int64_t i = 0; i < input.length; i++) { - out_data[i] = static_cast<out_type>(in_data[i] / factor); - if (bit_reader.IsSet() && (out_data[i] * factor != in_data[i])) { - RAISE_INVALID_CAST(in_data[i]); - } - bit_reader.Next(); - } - } else { - for (int64_t i = 0; i < input.length; i++) { - out_data[i] = static_cast<out_type>(in_data[i] / factor); - if (out_data[i] * factor != in_data[i]) { - RAISE_INVALID_CAST(in_data[i]); - } - } - } - -#undef RAISE_INVALID_CAST - } - } + + if (input.null_count != 0) { + BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); + for (int64_t i = 0; i < input.length; i++) { + out_data[i] = static_cast<out_type>(in_data[i] / factor); + if (bit_reader.IsSet() && (out_data[i] * factor != in_data[i])) { + RAISE_INVALID_CAST(in_data[i]); + } + bit_reader.Next(); + } + } else { + for (int64_t i = 0; i < input.length; i++) { + out_data[i] = static_cast<out_type>(in_data[i] / factor); + if (out_data[i] * factor != in_data[i]) { + RAISE_INVALID_CAST(in_data[i]); + } + } + } + +#undef RAISE_INVALID_CAST + } + } return Status::OK(); -} - -// <TimestampType, TimestampType> and <DurationType, DurationType> -template <typename O, typename I> -struct CastFunctor< - O, I, - enable_if_t<(is_timestamp_type<O>::value && is_timestamp_type<I>::value) || - (is_duration_type<O>::value && is_duration_type<I>::value)>> { +} + +// <TimestampType, TimestampType> and <DurationType, DurationType> +template <typename O, typename I> +struct CastFunctor< + O, I, + enable_if_t<(is_timestamp_type<O>::value && is_timestamp_type<I>::value) || + (is_duration_type<O>::value && is_duration_type<I>::value)>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - - const ArrayData& input = *batch[0].array(); - ArrayData* output = out->mutable_array(); - - // If units are the same, zero copy, otherwise convert - const auto& in_type = checked_cast<const I&>(*batch[0].type()); - const auto& out_type = checked_cast<const O&>(*output->type); - - // The units may be equal if the time zones are different. We might go to - // lengths to make this zero copy in the future but we leave it for now - - auto conversion = util::GetTimestampConversion(in_type.unit(), out_type.unit()); + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + + const ArrayData& input = *batch[0].array(); + ArrayData* output = out->mutable_array(); + + // If units are the same, zero copy, otherwise convert + const auto& in_type = checked_cast<const I&>(*batch[0].type()); + const auto& out_type = checked_cast<const O&>(*output->type); + + // The units may be equal if the time zones are different. We might go to + // lengths to make this zero copy in the future but we leave it for now + + auto conversion = util::GetTimestampConversion(in_type.unit(), out_type.unit()); return ShiftTime<int64_t, int64_t>(ctx, conversion.first, conversion.second, input, output); - } -}; - -template <> -struct CastFunctor<Date32Type, TimestampType> { + } +}; + +template <> +struct CastFunctor<Date32Type, TimestampType> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - - const ArrayData& input = *batch[0].array(); - ArrayData* output = out->mutable_array(); - - const auto& in_type = checked_cast<const TimestampType&>(*input.type); - - static const int64_t kTimestampToDateFactors[4] = { - 86400LL, // SECOND - 86400LL * 1000LL, // MILLI - 86400LL * 1000LL * 1000LL, // MICRO - 86400LL * 1000LL * 1000LL * 1000LL, // NANO - }; - - const int64_t factor = kTimestampToDateFactors[static_cast<int>(in_type.unit())]; + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + + const ArrayData& input = *batch[0].array(); + ArrayData* output = out->mutable_array(); + + const auto& in_type = checked_cast<const TimestampType&>(*input.type); + + static const int64_t kTimestampToDateFactors[4] = { + 86400LL, // SECOND + 86400LL * 1000LL, // MILLI + 86400LL * 1000LL * 1000LL, // MICRO + 86400LL * 1000LL * 1000LL * 1000LL, // NANO + }; + + const int64_t factor = kTimestampToDateFactors[static_cast<int>(in_type.unit())]; return ShiftTime<int64_t, int32_t>(ctx, util::DIVIDE, factor, input, output); - } -}; - -template <> -struct CastFunctor<Date64Type, TimestampType> { + } +}; + +template <> +struct CastFunctor<Date64Type, TimestampType> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - - const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; - const ArrayData& input = *batch[0].array(); - ArrayData* output = out->mutable_array(); - const auto& in_type = checked_cast<const TimestampType&>(*input.type); - - auto conversion = util::GetTimestampConversion(in_type.unit(), TimeUnit::MILLI); + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + + const CastOptions& options = checked_cast<const CastState&>(*ctx->state()).options; + const ArrayData& input = *batch[0].array(); + ArrayData* output = out->mutable_array(); + const auto& in_type = checked_cast<const TimestampType&>(*input.type); + + auto conversion = util::GetTimestampConversion(in_type.unit(), TimeUnit::MILLI); RETURN_NOT_OK((ShiftTime<int64_t, int64_t>(ctx, conversion.first, conversion.second, input, output))); - - // Ensure that intraday milliseconds have been zeroed out - auto out_data = output->GetMutableValues<int64_t>(1); - - if (input.null_count != 0) { - BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); - - for (int64_t i = 0; i < input.length; ++i) { - const int64_t remainder = out_data[i] % kMillisecondsInDay; - if (ARROW_PREDICT_FALSE(!options.allow_time_truncate && bit_reader.IsSet() && - remainder > 0)) { + + // Ensure that intraday milliseconds have been zeroed out + auto out_data = output->GetMutableValues<int64_t>(1); + + if (input.null_count != 0) { + BitmapReader bit_reader(input.buffers[0]->data(), input.offset, input.length); + + for (int64_t i = 0; i < input.length; ++i) { + const int64_t remainder = out_data[i] % kMillisecondsInDay; + if (ARROW_PREDICT_FALSE(!options.allow_time_truncate && bit_reader.IsSet() && + remainder > 0)) { return Status::Invalid("Timestamp value had non-zero intraday milliseconds"); - } - out_data[i] -= remainder; - bit_reader.Next(); - } - } else { - for (int64_t i = 0; i < input.length; ++i) { - const int64_t remainder = out_data[i] % kMillisecondsInDay; - if (ARROW_PREDICT_FALSE(!options.allow_time_truncate && remainder > 0)) { + } + out_data[i] -= remainder; + bit_reader.Next(); + } + } else { + for (int64_t i = 0; i < input.length; ++i) { + const int64_t remainder = out_data[i] % kMillisecondsInDay; + if (ARROW_PREDICT_FALSE(!options.allow_time_truncate && remainder > 0)) { return Status::Invalid("Timestamp value had non-zero intraday milliseconds"); - } - out_data[i] -= remainder; - } - } + } + out_data[i] -= remainder; + } + } return Status::OK(); - } -}; - -// ---------------------------------------------------------------------- -// From one time32 or time64 to another - -template <typename O, typename I> -struct CastFunctor<O, I, enable_if_t<is_time_type<I>::value && is_time_type<O>::value>> { - using in_t = typename I::c_type; - using out_t = typename O::c_type; - + } +}; + +// ---------------------------------------------------------------------- +// From one time32 or time64 to another + +template <typename O, typename I> +struct CastFunctor<O, I, enable_if_t<is_time_type<I>::value && is_time_type<O>::value>> { + using in_t = typename I::c_type; + using out_t = typename O::c_type; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - - const ArrayData& input = *batch[0].array(); - ArrayData* output = out->mutable_array(); - - // If units are the same, zero copy, otherwise convert - const auto& in_type = checked_cast<const I&>(*input.type); - const auto& out_type = checked_cast<const O&>(*output->type); - DCHECK_NE(in_type.unit(), out_type.unit()) << "Do not cast equal types"; - auto conversion = util::GetTimestampConversion(in_type.unit(), out_type.unit()); + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + + const ArrayData& input = *batch[0].array(); + ArrayData* output = out->mutable_array(); + + // If units are the same, zero copy, otherwise convert + const auto& in_type = checked_cast<const I&>(*input.type); + const auto& out_type = checked_cast<const O&>(*output->type); + DCHECK_NE(in_type.unit(), out_type.unit()) << "Do not cast equal types"; + auto conversion = util::GetTimestampConversion(in_type.unit(), out_type.unit()); return ShiftTime<in_t, out_t>(ctx, conversion.first, conversion.second, input, output); - } -}; - -// ---------------------------------------------------------------------- -// Between date32 and date64 - -template <> -struct CastFunctor<Date64Type, Date32Type> { + } +}; + +// ---------------------------------------------------------------------- +// Between date32 and date64 + +template <> +struct CastFunctor<Date64Type, Date32Type> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + return ShiftTime<int32_t, int64_t>(ctx, util::MULTIPLY, kMillisecondsInDay, *batch[0].array(), out->mutable_array()); - } -}; - -template <> -struct CastFunctor<Date32Type, Date64Type> { + } +}; + +template <> +struct CastFunctor<Date32Type, Date64Type> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DCHECK_EQ(batch[0].kind(), Datum::ARRAY); - + DCHECK_EQ(batch[0].kind(), Datum::ARRAY); + return ShiftTime<int64_t, int32_t>(ctx, util::DIVIDE, kMillisecondsInDay, *batch[0].array(), out->mutable_array()); - } -}; - -// ---------------------------------------------------------------------- + } +}; + +// ---------------------------------------------------------------------- // date32, date64 to timestamp template <> @@ -289,164 +289,164 @@ struct CastFunctor<TimestampType, Date64Type> { }; // ---------------------------------------------------------------------- -// String to Timestamp - -struct ParseTimestamp { - template <typename OutValue, typename Arg0Value> +// String to Timestamp + +struct ParseTimestamp { + template <typename OutValue, typename Arg0Value> OutValue Call(KernelContext*, Arg0Value val, Status* st) const { - OutValue result = 0; - if (ARROW_PREDICT_FALSE(!ParseValue(type, val.data(), val.size(), &result))) { + OutValue result = 0; + if (ARROW_PREDICT_FALSE(!ParseValue(type, val.data(), val.size(), &result))) { *st = Status::Invalid("Failed to parse string: '", val, "' as a scalar of type ", type.ToString()); - } - return result; - } - - const TimestampType& type; -}; - -template <typename I> -struct CastFunctor<TimestampType, I, enable_if_t<is_base_binary_type<I>::value>> { + } + return result; + } + + const TimestampType& type; +}; + +template <typename I> +struct CastFunctor<TimestampType, I, enable_if_t<is_base_binary_type<I>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const auto& out_type = checked_cast<const TimestampType&>(*out->type()); - applicator::ScalarUnaryNotNullStateful<TimestampType, I, ParseTimestamp> kernel( - ParseTimestamp{out_type}); - return kernel.Exec(ctx, batch, out); - } -}; - -template <typename Type> -void AddCrossUnitCast(CastFunction* func) { - ScalarKernel kernel; + const auto& out_type = checked_cast<const TimestampType&>(*out->type()); + applicator::ScalarUnaryNotNullStateful<TimestampType, I, ParseTimestamp> kernel( + ParseTimestamp{out_type}); + return kernel.Exec(ctx, batch, out); + } +}; + +template <typename Type> +void AddCrossUnitCast(CastFunction* func) { + ScalarKernel kernel; kernel.exec = TrivialScalarUnaryAsArraysExec(CastFunctor<Type, Type>::Exec); - kernel.signature = KernelSignature::Make({InputType(Type::type_id)}, kOutputTargetType); - DCHECK_OK(func->AddKernel(Type::type_id, std::move(kernel))); -} - -std::shared_ptr<CastFunction> GetDate32Cast() { - auto func = std::make_shared<CastFunction>("cast_date32", Type::DATE32); - auto out_ty = date32(); - AddCommonCasts(Type::DATE32, out_ty, func.get()); - - // int32 -> date32 - AddZeroCopyCast(Type::INT32, int32(), date32(), func.get()); - - // date64 -> date32 - AddSimpleCast<Date64Type, Date32Type>(date64(), date32(), func.get()); - - // timestamp -> date32 - AddSimpleCast<TimestampType, Date32Type>(InputType(Type::TIMESTAMP), date32(), - func.get()); - return func; -} - -std::shared_ptr<CastFunction> GetDate64Cast() { - auto func = std::make_shared<CastFunction>("cast_date64", Type::DATE64); - auto out_ty = date64(); - AddCommonCasts(Type::DATE64, out_ty, func.get()); - - // int64 -> date64 - AddZeroCopyCast(Type::INT64, int64(), date64(), func.get()); - - // date32 -> date64 - AddSimpleCast<Date32Type, Date64Type>(date32(), date64(), func.get()); - - // timestamp -> date64 - AddSimpleCast<TimestampType, Date64Type>(InputType(Type::TIMESTAMP), date64(), - func.get()); - return func; -} - -std::shared_ptr<CastFunction> GetDurationCast() { - auto func = std::make_shared<CastFunction>("cast_duration", Type::DURATION); - AddCommonCasts(Type::DURATION, kOutputTargetType, func.get()); - - auto seconds = duration(TimeUnit::SECOND); - auto millis = duration(TimeUnit::MILLI); - auto micros = duration(TimeUnit::MICRO); - auto nanos = duration(TimeUnit::NANO); - - // Same integer representation - AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); - - // Between durations - AddCrossUnitCast<DurationType>(func.get()); - - return func; -} - -std::shared_ptr<CastFunction> GetTime32Cast() { - auto func = std::make_shared<CastFunction>("cast_time32", Type::TIME32); - AddCommonCasts(Type::TIME32, kOutputTargetType, func.get()); - - // Zero copy when the unit is the same or same integer representation - AddZeroCopyCast(Type::INT32, /*in_type=*/int32(), kOutputTargetType, func.get()); - - // time64 -> time32 - AddSimpleCast<Time64Type, Time32Type>(InputType(Type::TIME64), kOutputTargetType, - func.get()); - - // time32 -> time32 - AddCrossUnitCast<Time32Type>(func.get()); - - return func; -} - -std::shared_ptr<CastFunction> GetTime64Cast() { - auto func = std::make_shared<CastFunction>("cast_time64", Type::TIME64); - AddCommonCasts(Type::TIME64, kOutputTargetType, func.get()); - - // Zero copy when the unit is the same or same integer representation - AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); - - // time32 -> time64 - AddSimpleCast<Time32Type, Time64Type>(InputType(Type::TIME32), kOutputTargetType, - func.get()); - - // Between durations - AddCrossUnitCast<Time64Type>(func.get()); - - return func; -} - -std::shared_ptr<CastFunction> GetTimestampCast() { - auto func = std::make_shared<CastFunction>("cast_timestamp", Type::TIMESTAMP); - AddCommonCasts(Type::TIMESTAMP, kOutputTargetType, func.get()); - - // Same integer representation - AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); - - // From date types + kernel.signature = KernelSignature::Make({InputType(Type::type_id)}, kOutputTargetType); + DCHECK_OK(func->AddKernel(Type::type_id, std::move(kernel))); +} + +std::shared_ptr<CastFunction> GetDate32Cast() { + auto func = std::make_shared<CastFunction>("cast_date32", Type::DATE32); + auto out_ty = date32(); + AddCommonCasts(Type::DATE32, out_ty, func.get()); + + // int32 -> date32 + AddZeroCopyCast(Type::INT32, int32(), date32(), func.get()); + + // date64 -> date32 + AddSimpleCast<Date64Type, Date32Type>(date64(), date32(), func.get()); + + // timestamp -> date32 + AddSimpleCast<TimestampType, Date32Type>(InputType(Type::TIMESTAMP), date32(), + func.get()); + return func; +} + +std::shared_ptr<CastFunction> GetDate64Cast() { + auto func = std::make_shared<CastFunction>("cast_date64", Type::DATE64); + auto out_ty = date64(); + AddCommonCasts(Type::DATE64, out_ty, func.get()); + + // int64 -> date64 + AddZeroCopyCast(Type::INT64, int64(), date64(), func.get()); + + // date32 -> date64 + AddSimpleCast<Date32Type, Date64Type>(date32(), date64(), func.get()); + + // timestamp -> date64 + AddSimpleCast<TimestampType, Date64Type>(InputType(Type::TIMESTAMP), date64(), + func.get()); + return func; +} + +std::shared_ptr<CastFunction> GetDurationCast() { + auto func = std::make_shared<CastFunction>("cast_duration", Type::DURATION); + AddCommonCasts(Type::DURATION, kOutputTargetType, func.get()); + + auto seconds = duration(TimeUnit::SECOND); + auto millis = duration(TimeUnit::MILLI); + auto micros = duration(TimeUnit::MICRO); + auto nanos = duration(TimeUnit::NANO); + + // Same integer representation + AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); + + // Between durations + AddCrossUnitCast<DurationType>(func.get()); + + return func; +} + +std::shared_ptr<CastFunction> GetTime32Cast() { + auto func = std::make_shared<CastFunction>("cast_time32", Type::TIME32); + AddCommonCasts(Type::TIME32, kOutputTargetType, func.get()); + + // Zero copy when the unit is the same or same integer representation + AddZeroCopyCast(Type::INT32, /*in_type=*/int32(), kOutputTargetType, func.get()); + + // time64 -> time32 + AddSimpleCast<Time64Type, Time32Type>(InputType(Type::TIME64), kOutputTargetType, + func.get()); + + // time32 -> time32 + AddCrossUnitCast<Time32Type>(func.get()); + + return func; +} + +std::shared_ptr<CastFunction> GetTime64Cast() { + auto func = std::make_shared<CastFunction>("cast_time64", Type::TIME64); + AddCommonCasts(Type::TIME64, kOutputTargetType, func.get()); + + // Zero copy when the unit is the same or same integer representation + AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); + + // time32 -> time64 + AddSimpleCast<Time32Type, Time64Type>(InputType(Type::TIME32), kOutputTargetType, + func.get()); + + // Between durations + AddCrossUnitCast<Time64Type>(func.get()); + + return func; +} + +std::shared_ptr<CastFunction> GetTimestampCast() { + auto func = std::make_shared<CastFunction>("cast_timestamp", Type::TIMESTAMP); + AddCommonCasts(Type::TIMESTAMP, kOutputTargetType, func.get()); + + // Same integer representation + AddZeroCopyCast(Type::INT64, /*in_type=*/int64(), kOutputTargetType, func.get()); + + // From date types // TODO: ARROW-8876, these casts are not directly tested AddSimpleCast<Date32Type, TimestampType>(InputType(Type::DATE32), kOutputTargetType, func.get()); AddSimpleCast<Date64Type, TimestampType>(InputType(Type::DATE64), kOutputTargetType, func.get()); - - // string -> timestamp - AddSimpleCast<StringType, TimestampType>(utf8(), kOutputTargetType, func.get()); - // large_string -> timestamp - AddSimpleCast<LargeStringType, TimestampType>(large_utf8(), kOutputTargetType, - func.get()); - - // From one timestamp to another - AddCrossUnitCast<TimestampType>(func.get()); - - return func; -} - -std::vector<std::shared_ptr<CastFunction>> GetTemporalCasts() { - std::vector<std::shared_ptr<CastFunction>> functions; - - functions.push_back(GetDate32Cast()); - functions.push_back(GetDate64Cast()); - functions.push_back(GetDurationCast()); - functions.push_back(GetTime32Cast()); - functions.push_back(GetTime64Cast()); - functions.push_back(GetTimestampCast()); - return functions; -} - -} // namespace internal -} // namespace compute -} // namespace arrow + + // string -> timestamp + AddSimpleCast<StringType, TimestampType>(utf8(), kOutputTargetType, func.get()); + // large_string -> timestamp + AddSimpleCast<LargeStringType, TimestampType>(large_utf8(), kOutputTargetType, + func.get()); + + // From one timestamp to another + AddCrossUnitCast<TimestampType>(func.get()); + + return func; +} + +std::vector<std::shared_ptr<CastFunction>> GetTemporalCasts() { + std::vector<std::shared_ptr<CastFunction>> functions; + + functions.push_back(GetDate32Cast()); + functions.push_back(GetDate64Cast()); + functions.push_back(GetDurationCast()); + functions.push_back(GetTime32Cast()); + functions.push_back(GetTime64Cast()); + functions.push_back(GetTimestampCast()); + return functions; +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_compare.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_compare.cc index 713875937a..4342d776c3 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_compare.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_compare.cc @@ -1,70 +1,70 @@ -// 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. - +// 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. + #include <cmath> #include <limits> #include "arrow/compute/api_scalar.h" -#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/common.h" #include "arrow/util/bitmap_ops.h" - -namespace arrow { - -using internal::checked_cast; -using internal::checked_pointer_cast; -using util::string_view; - -namespace compute { -namespace internal { - -namespace { - -struct Equal { + +namespace arrow { + +using internal::checked_cast; +using internal::checked_pointer_cast; +using util::string_view; + +namespace compute { +namespace internal { + +namespace { + +struct Equal { template <typename T, typename Arg0, typename Arg1> static constexpr T Call(KernelContext*, const Arg0& left, const Arg1& right, Status*) { static_assert(std::is_same<T, bool>::value && std::is_same<Arg0, Arg1>::value, ""); - return left == right; - } -}; - -struct NotEqual { + return left == right; + } +}; + +struct NotEqual { template <typename T, typename Arg0, typename Arg1> static constexpr T Call(KernelContext*, const Arg0& left, const Arg1& right, Status*) { static_assert(std::is_same<T, bool>::value && std::is_same<Arg0, Arg1>::value, ""); - return left != right; - } -}; - -struct Greater { + return left != right; + } +}; + +struct Greater { template <typename T, typename Arg0, typename Arg1> static constexpr T Call(KernelContext*, const Arg0& left, const Arg1& right, Status*) { static_assert(std::is_same<T, bool>::value && std::is_same<Arg0, Arg1>::value, ""); - return left > right; - } -}; - -struct GreaterEqual { + return left > right; + } +}; + +struct GreaterEqual { template <typename T, typename Arg0, typename Arg1> static constexpr T Call(KernelContext*, const Arg0& left, const Arg1& right, Status*) { static_assert(std::is_same<T, bool>::value && std::is_same<Arg0, Arg1>::value, ""); - return left >= right; - } -}; - + return left >= right; + } +}; + template <typename T> using is_unsigned_integer = std::integral_constant<bool, std::is_integral<T>::value && std::is_unsigned<T>::value>; @@ -138,22 +138,22 @@ struct Maximum { } }; -// Implement Less, LessEqual by flipping arguments to Greater, GreaterEqual - -template <typename Op> -void AddIntegerCompare(const std::shared_ptr<DataType>& ty, ScalarFunction* func) { - auto exec = - GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>(*ty); - DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec))); -} - -template <typename InType, typename Op> -void AddGenericCompare(const std::shared_ptr<DataType>& ty, ScalarFunction* func) { - DCHECK_OK( - func->AddKernel({ty, ty}, boolean(), - applicator::ScalarBinaryEqualTypes<BooleanType, InType, Op>::Exec)); -} - +// Implement Less, LessEqual by flipping arguments to Greater, GreaterEqual + +template <typename Op> +void AddIntegerCompare(const std::shared_ptr<DataType>& ty, ScalarFunction* func) { + auto exec = + GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>(*ty); + DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec))); +} + +template <typename InType, typename Op> +void AddGenericCompare(const std::shared_ptr<DataType>& ty, ScalarFunction* func) { + DCHECK_OK( + func->AddKernel({ty, ty}, boolean(), + applicator::ScalarBinaryEqualTypes<BooleanType, InType, Op>::Exec)); +} + struct CompareFunction : ScalarFunction { using ScalarFunction::ScalarFunction; @@ -201,79 +201,79 @@ struct VarArgsCompareFunction : ScalarFunction { } }; -template <typename Op> +template <typename Op> std::shared_ptr<ScalarFunction> MakeCompareFunction(std::string name, const FunctionDoc* doc) { auto func = std::make_shared<CompareFunction>(name, Arity::Binary(), doc); - - DCHECK_OK(func->AddKernel( - {boolean(), boolean()}, boolean(), - applicator::ScalarBinary<BooleanType, BooleanType, BooleanType, Op>::Exec)); - - for (const std::shared_ptr<DataType>& ty : IntTypes()) { - AddIntegerCompare<Op>(ty, func.get()); - } - AddIntegerCompare<Op>(date32(), func.get()); - AddIntegerCompare<Op>(date64(), func.get()); - - AddGenericCompare<FloatType, Op>(float32(), func.get()); - AddGenericCompare<DoubleType, Op>(float64(), func.get()); - - // Add timestamp kernels - for (auto unit : AllTimeUnits()) { - InputType in_type(match::TimestampTypeUnit(unit)); - auto exec = - GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( - int64()); - DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); - } - - // Duration - for (auto unit : AllTimeUnits()) { - InputType in_type(match::DurationTypeUnit(unit)); - auto exec = - GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( - int64()); - DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); - } - - // Time32 and Time64 - for (auto unit : {TimeUnit::SECOND, TimeUnit::MILLI}) { - InputType in_type(match::Time32TypeUnit(unit)); - auto exec = - GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( - int32()); - DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); - } - for (auto unit : {TimeUnit::MICRO, TimeUnit::NANO}) { - InputType in_type(match::Time64TypeUnit(unit)); - auto exec = - GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( - int64()); - DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); - } - - for (const std::shared_ptr<DataType>& ty : BaseBinaryTypes()) { - auto exec = - GenerateVarBinaryBase<applicator::ScalarBinaryEqualTypes, BooleanType, Op>(*ty); - DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec))); - } - - return func; -} - -std::shared_ptr<ScalarFunction> MakeFlippedFunction(std::string name, + + DCHECK_OK(func->AddKernel( + {boolean(), boolean()}, boolean(), + applicator::ScalarBinary<BooleanType, BooleanType, BooleanType, Op>::Exec)); + + for (const std::shared_ptr<DataType>& ty : IntTypes()) { + AddIntegerCompare<Op>(ty, func.get()); + } + AddIntegerCompare<Op>(date32(), func.get()); + AddIntegerCompare<Op>(date64(), func.get()); + + AddGenericCompare<FloatType, Op>(float32(), func.get()); + AddGenericCompare<DoubleType, Op>(float64(), func.get()); + + // Add timestamp kernels + for (auto unit : AllTimeUnits()) { + InputType in_type(match::TimestampTypeUnit(unit)); + auto exec = + GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( + int64()); + DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); + } + + // Duration + for (auto unit : AllTimeUnits()) { + InputType in_type(match::DurationTypeUnit(unit)); + auto exec = + GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( + int64()); + DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); + } + + // Time32 and Time64 + for (auto unit : {TimeUnit::SECOND, TimeUnit::MILLI}) { + InputType in_type(match::Time32TypeUnit(unit)); + auto exec = + GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( + int32()); + DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); + } + for (auto unit : {TimeUnit::MICRO, TimeUnit::NANO}) { + InputType in_type(match::Time64TypeUnit(unit)); + auto exec = + GeneratePhysicalInteger<applicator::ScalarBinaryEqualTypes, BooleanType, Op>( + int64()); + DCHECK_OK(func->AddKernel({in_type, in_type}, boolean(), std::move(exec))); + } + + for (const std::shared_ptr<DataType>& ty : BaseBinaryTypes()) { + auto exec = + GenerateVarBinaryBase<applicator::ScalarBinaryEqualTypes, BooleanType, Op>(*ty); + DCHECK_OK(func->AddKernel({ty, ty}, boolean(), std::move(exec))); + } + + return func; +} + +std::shared_ptr<ScalarFunction> MakeFlippedFunction(std::string name, const ScalarFunction& func, const FunctionDoc* doc) { auto flipped_func = std::make_shared<CompareFunction>(name, Arity::Binary(), doc); - for (const ScalarKernel* kernel : func.kernels()) { - ScalarKernel flipped_kernel = *kernel; - flipped_kernel.exec = MakeFlippedBinaryExec(kernel->exec); - DCHECK_OK(flipped_func->AddKernel(std::move(flipped_kernel))); - } - return flipped_func; -} - + for (const ScalarKernel* kernel : func.kernels()) { + ScalarKernel flipped_kernel = *kernel; + flipped_kernel.exec = MakeFlippedBinaryExec(kernel->exec); + DCHECK_OK(flipped_func->AddKernel(std::move(flipped_kernel))); + } + return flipped_func; +} + using MinMaxState = OptionsWrapper<ElementWiseAggregateOptions>; // Implement a variadic scalar min/max kernel. @@ -489,23 +489,23 @@ const FunctionDoc max_element_wise_doc{ "NaN will be taken over null, but not over any valid float."), {"*args"}, "ElementWiseAggregateOptions"}; -} // namespace - -void RegisterScalarComparison(FunctionRegistry* registry) { +} // namespace + +void RegisterScalarComparison(FunctionRegistry* registry) { DCHECK_OK(registry->AddFunction(MakeCompareFunction<Equal>("equal", &equal_doc))); DCHECK_OK( registry->AddFunction(MakeCompareFunction<NotEqual>("not_equal", ¬_equal_doc))); - + auto greater = MakeCompareFunction<Greater>("greater", &greater_doc); auto greater_equal = MakeCompareFunction<GreaterEqual>("greater_equal", &greater_equal_doc); - + auto less = MakeFlippedFunction("less", *greater, &less_doc); auto less_equal = MakeFlippedFunction("less_equal", *greater_equal, &less_equal_doc); - DCHECK_OK(registry->AddFunction(std::move(less))); - DCHECK_OK(registry->AddFunction(std::move(less_equal))); - DCHECK_OK(registry->AddFunction(std::move(greater))); - DCHECK_OK(registry->AddFunction(std::move(greater_equal))); + DCHECK_OK(registry->AddFunction(std::move(less))); + DCHECK_OK(registry->AddFunction(std::move(less_equal))); + DCHECK_OK(registry->AddFunction(std::move(greater))); + DCHECK_OK(registry->AddFunction(std::move(greater_equal))); // ---------------------------------------------------------------------- // Variadic element-wise functions @@ -517,8 +517,8 @@ void RegisterScalarComparison(FunctionRegistry* registry) { auto max_element_wise = MakeScalarMinMax<Maximum>("max_element_wise", &max_element_wise_doc); DCHECK_OK(registry->AddFunction(std::move(max_element_wise))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_fill_null.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_fill_null.cc index e189c294be..cf22b0de3d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_fill_null.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_fill_null.cc @@ -1,157 +1,157 @@ -// 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. - -#include <algorithm> -#include <cstring> - -#include "arrow/compute/kernels/common.h" -#include "arrow/scalar.h" -#include "arrow/util/bit_block_counter.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_ops.h" - -namespace arrow { - -using internal::BitBlockCount; -using internal::BitBlockCounter; - -namespace compute { -namespace internal { - -namespace { - -template <typename Type, typename Enable = void> -struct FillNullFunctor {}; - +// 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. + +#include <algorithm> +#include <cstring> + +#include "arrow/compute/kernels/common.h" +#include "arrow/scalar.h" +#include "arrow/util/bit_block_counter.h" +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_ops.h" + +namespace arrow { + +using internal::BitBlockCount; +using internal::BitBlockCounter; + +namespace compute { +namespace internal { + +namespace { + +template <typename Type, typename Enable = void> +struct FillNullFunctor {}; + // Numeric inputs -template <typename Type> -struct FillNullFunctor<Type, enable_if_t<is_number_type<Type>::value>> { - using T = typename TypeTraits<Type>::CType; - +template <typename Type> +struct FillNullFunctor<Type, enable_if_t<is_number_type<Type>::value>> { + using T = typename TypeTraits<Type>::CType; + static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const ArrayData& data = *batch[0].array(); - const Scalar& fill_value = *batch[1].scalar(); - ArrayData* output = out->mutable_array(); - - // Ensure the kernel is configured properly to have no validity bitmap / - // null count 0 unless we explicitly propagate it below. - DCHECK(output->buffers[0] == nullptr); - - T value = UnboxScalar<Type>::Unbox(fill_value); - if (data.MayHaveNulls() != 0 && fill_value.is_valid) { + const ArrayData& data = *batch[0].array(); + const Scalar& fill_value = *batch[1].scalar(); + ArrayData* output = out->mutable_array(); + + // Ensure the kernel is configured properly to have no validity bitmap / + // null count 0 unless we explicitly propagate it below. + DCHECK(output->buffers[0] == nullptr); + + T value = UnboxScalar<Type>::Unbox(fill_value); + if (data.MayHaveNulls() != 0 && fill_value.is_valid) { ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Buffer> out_buf, ctx->Allocate(data.length * sizeof(T))); - - const uint8_t* is_valid = data.buffers[0]->data(); - const T* in_values = data.GetValues<T>(1); - T* out_values = reinterpret_cast<T*>(out_buf->mutable_data()); - int64_t offset = data.offset; - BitBlockCounter bit_counter(is_valid, data.offset, data.length); - while (offset < data.offset + data.length) { - BitBlockCount block = bit_counter.NextWord(); - if (block.AllSet()) { - // Block all not null - std::memcpy(out_values, in_values, block.length * sizeof(T)); - } else if (block.NoneSet()) { - // Block all null - std::fill(out_values, out_values + block.length, value); - } else { - for (int64_t i = 0; i < block.length; ++i) { - out_values[i] = BitUtil::GetBit(is_valid, offset + i) ? in_values[i] : value; - } - } - offset += block.length; - out_values += block.length; - in_values += block.length; - } - output->buffers[1] = out_buf; + + const uint8_t* is_valid = data.buffers[0]->data(); + const T* in_values = data.GetValues<T>(1); + T* out_values = reinterpret_cast<T*>(out_buf->mutable_data()); + int64_t offset = data.offset; + BitBlockCounter bit_counter(is_valid, data.offset, data.length); + while (offset < data.offset + data.length) { + BitBlockCount block = bit_counter.NextWord(); + if (block.AllSet()) { + // Block all not null + std::memcpy(out_values, in_values, block.length * sizeof(T)); + } else if (block.NoneSet()) { + // Block all null + std::fill(out_values, out_values + block.length, value); + } else { + for (int64_t i = 0; i < block.length; ++i) { + out_values[i] = BitUtil::GetBit(is_valid, offset + i) ? in_values[i] : value; + } + } + offset += block.length; + out_values += block.length; + in_values += block.length; + } + output->buffers[1] = out_buf; output->null_count = 0; - } else { - *output = data; - } + } else { + *output = data; + } return Status::OK(); - } -}; - + } +}; + // Boolean input -template <typename Type> -struct FillNullFunctor<Type, enable_if_t<is_boolean_type<Type>::value>> { +template <typename Type> +struct FillNullFunctor<Type, enable_if_t<is_boolean_type<Type>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const ArrayData& data = *batch[0].array(); - const Scalar& fill_value = *batch[1].scalar(); - ArrayData* output = out->mutable_array(); - - bool value = UnboxScalar<BooleanType>::Unbox(fill_value); - if (data.MayHaveNulls() != 0 && fill_value.is_valid) { + const ArrayData& data = *batch[0].array(); + const Scalar& fill_value = *batch[1].scalar(); + ArrayData* output = out->mutable_array(); + + bool value = UnboxScalar<BooleanType>::Unbox(fill_value); + if (data.MayHaveNulls() != 0 && fill_value.is_valid) { ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Buffer> out_buf, ctx->AllocateBitmap(data.length)); - - const uint8_t* is_valid = data.buffers[0]->data(); - const uint8_t* data_bitmap = data.buffers[1]->data(); - uint8_t* out_bitmap = out_buf->mutable_data(); - - int64_t data_offset = data.offset; - BitBlockCounter bit_counter(is_valid, data.offset, data.length); - - int64_t out_offset = 0; - while (out_offset < data.length) { - BitBlockCount block = bit_counter.NextWord(); - if (block.AllSet()) { - // Block all not null - ::arrow::internal::CopyBitmap(data_bitmap, data_offset, block.length, - out_bitmap, out_offset); - } else if (block.NoneSet()) { - // Block all null - BitUtil::SetBitsTo(out_bitmap, out_offset, block.length, value); - } else { - for (int64_t i = 0; i < block.length; ++i) { - BitUtil::SetBitTo(out_bitmap, out_offset + i, - BitUtil::GetBit(is_valid, data_offset + i) - ? BitUtil::GetBit(data_bitmap, data_offset + i) - : value); - } - } - data_offset += block.length; - out_offset += block.length; - } - output->buffers[1] = out_buf; + + const uint8_t* is_valid = data.buffers[0]->data(); + const uint8_t* data_bitmap = data.buffers[1]->data(); + uint8_t* out_bitmap = out_buf->mutable_data(); + + int64_t data_offset = data.offset; + BitBlockCounter bit_counter(is_valid, data.offset, data.length); + + int64_t out_offset = 0; + while (out_offset < data.length) { + BitBlockCount block = bit_counter.NextWord(); + if (block.AllSet()) { + // Block all not null + ::arrow::internal::CopyBitmap(data_bitmap, data_offset, block.length, + out_bitmap, out_offset); + } else if (block.NoneSet()) { + // Block all null + BitUtil::SetBitsTo(out_bitmap, out_offset, block.length, value); + } else { + for (int64_t i = 0; i < block.length; ++i) { + BitUtil::SetBitTo(out_bitmap, out_offset + i, + BitUtil::GetBit(is_valid, data_offset + i) + ? BitUtil::GetBit(data_bitmap, data_offset + i) + : value); + } + } + data_offset += block.length; + out_offset += block.length; + } + output->buffers[1] = out_buf; output->null_count = 0; - } else { - *output = data; - } + } else { + *output = data; + } return Status::OK(); - } -}; - + } +}; + // Null input -template <typename Type> -struct FillNullFunctor<Type, enable_if_t<is_null_type<Type>::value>> { +template <typename Type> +struct FillNullFunctor<Type, enable_if_t<is_null_type<Type>::value>> { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - // Nothing preallocated, so we assign into the output - *out->mutable_array() = *batch[0].array(); + // Nothing preallocated, so we assign into the output + *out->mutable_array() = *batch[0].array(); return Status::OK(); - } -}; - + } +}; + // Binary-like input template <typename Type> @@ -194,20 +194,20 @@ struct FillNullFunctor<Type, enable_if_t<is_base_binary_type<Type>::value>> { } }; -void AddBasicFillNullKernels(ScalarKernel kernel, ScalarFunction* func) { - auto AddKernels = [&](const std::vector<std::shared_ptr<DataType>>& types) { - for (const std::shared_ptr<DataType>& ty : types) { - kernel.signature = - KernelSignature::Make({InputType::Array(ty), InputType::Scalar(ty)}, ty); - kernel.exec = GenerateTypeAgnosticPrimitive<FillNullFunctor>(*ty); - DCHECK_OK(func->AddKernel(kernel)); - } - }; - AddKernels(NumericTypes()); - AddKernels(TemporalTypes()); - AddKernels({boolean(), null()}); -} - +void AddBasicFillNullKernels(ScalarKernel kernel, ScalarFunction* func) { + auto AddKernels = [&](const std::vector<std::shared_ptr<DataType>>& types) { + for (const std::shared_ptr<DataType>& ty : types) { + kernel.signature = + KernelSignature::Make({InputType::Array(ty), InputType::Scalar(ty)}, ty); + kernel.exec = GenerateTypeAgnosticPrimitive<FillNullFunctor>(*ty); + DCHECK_OK(func->AddKernel(kernel)); + } + }; + AddKernels(NumericTypes()); + AddKernels(TemporalTypes()); + AddKernels({boolean(), null()}); +} + void AddBinaryFillNullKernels(ScalarKernel kernel, ScalarFunction* func) { for (const std::shared_ptr<DataType>& ty : BaseBinaryTypes()) { kernel.signature = @@ -224,21 +224,21 @@ const FunctionDoc fill_null_doc{ "Each null value in `values` is replaced with `fill_value`."), {"values", "fill_value"}}; -} // namespace - -void RegisterScalarFillNull(FunctionRegistry* registry) { - { - ScalarKernel fill_null_base; - fill_null_base.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; - fill_null_base.mem_allocation = MemAllocation::NO_PREALLOCATE; +} // namespace + +void RegisterScalarFillNull(FunctionRegistry* registry) { + { + ScalarKernel fill_null_base; + fill_null_base.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; + fill_null_base.mem_allocation = MemAllocation::NO_PREALLOCATE; auto fill_null = std::make_shared<ScalarFunction>("fill_null", Arity::Binary(), &fill_null_doc); - AddBasicFillNullKernels(fill_null_base, fill_null.get()); + AddBasicFillNullKernels(fill_null_base, fill_null.get()); AddBinaryFillNullKernels(fill_null_base, fill_null.get()); - DCHECK_OK(registry->AddFunction(fill_null)); - } -} - -} // namespace internal -} // namespace compute -} // namespace arrow + DCHECK_OK(registry->AddFunction(fill_null)); + } +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_nested.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_nested.cc index cae2df4a09..e9f0696c8f 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_nested.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_nested.cc @@ -1,60 +1,60 @@ -// 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. - -// Vector kernels involving nested types - -#include "arrow/array/array_base.h" +// 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. + +// Vector kernels involving nested types + +#include "arrow/array/array_base.h" #include "arrow/compute/api_scalar.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/result.h" -#include "arrow/util/bit_block_counter.h" - -namespace arrow { -namespace compute { -namespace internal { -namespace { - -template <typename Type, typename offset_type = typename Type::offset_type> +#include "arrow/compute/kernels/common.h" +#include "arrow/result.h" +#include "arrow/util/bit_block_counter.h" + +namespace arrow { +namespace compute { +namespace internal { +namespace { + +template <typename Type, typename offset_type = typename Type::offset_type> Status ListValueLength(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - using ScalarType = typename TypeTraits<Type>::ScalarType; - using OffsetScalarType = typename TypeTraits<Type>::OffsetScalarType; - - if (batch[0].kind() == Datum::ARRAY) { - typename TypeTraits<Type>::ArrayType list(batch[0].array()); - ArrayData* out_arr = out->mutable_array(); - auto out_values = out_arr->GetMutableValues<offset_type>(1); - const offset_type* offsets = list.raw_value_offsets(); - ::arrow::internal::VisitBitBlocksVoid( - list.data()->buffers[0], list.offset(), list.length(), - [&](int64_t position) { - *out_values++ = offsets[position + 1] - offsets[position]; - }, - [&]() { *out_values++ = 0; }); - } else { - const auto& arg0 = batch[0].scalar_as<ScalarType>(); - if (arg0.is_valid) { - checked_cast<OffsetScalarType*>(out->scalar().get())->value = - static_cast<offset_type>(arg0.value->length()); - } - } + using ScalarType = typename TypeTraits<Type>::ScalarType; + using OffsetScalarType = typename TypeTraits<Type>::OffsetScalarType; + + if (batch[0].kind() == Datum::ARRAY) { + typename TypeTraits<Type>::ArrayType list(batch[0].array()); + ArrayData* out_arr = out->mutable_array(); + auto out_values = out_arr->GetMutableValues<offset_type>(1); + const offset_type* offsets = list.raw_value_offsets(); + ::arrow::internal::VisitBitBlocksVoid( + list.data()->buffers[0], list.offset(), list.length(), + [&](int64_t position) { + *out_values++ = offsets[position + 1] - offsets[position]; + }, + [&]() { *out_values++ = 0; }); + } else { + const auto& arg0 = batch[0].scalar_as<ScalarType>(); + if (arg0.is_valid) { + checked_cast<OffsetScalarType*>(out->scalar().get())->value = + static_cast<offset_type>(arg0.value->length()); + } + } return Status::OK(); -} - +} + const FunctionDoc list_value_length_doc{ "Compute list lengths", ("`lists` must have a list-like type.\n" @@ -154,16 +154,16 @@ const FunctionDoc make_struct_doc{"Wrap Arrays into a StructArray", {"*args"}, "MakeStructOptions"}; -} // namespace - -void RegisterScalarNested(FunctionRegistry* registry) { +} // namespace + +void RegisterScalarNested(FunctionRegistry* registry) { auto list_value_length = std::make_shared<ScalarFunction>( "list_value_length", Arity::Unary(), &list_value_length_doc); - DCHECK_OK(list_value_length->AddKernel({InputType(Type::LIST)}, int32(), - ListValueLength<ListType>)); - DCHECK_OK(list_value_length->AddKernel({InputType(Type::LARGE_LIST)}, int64(), - ListValueLength<LargeListType>)); - DCHECK_OK(registry->AddFunction(std::move(list_value_length))); + DCHECK_OK(list_value_length->AddKernel({InputType(Type::LIST)}, int32(), + ListValueLength<ListType>)); + DCHECK_OK(list_value_length->AddKernel({InputType(Type::LARGE_LIST)}, int64(), + ListValueLength<LargeListType>)); + DCHECK_OK(registry->AddFunction(std::move(list_value_length))); static MakeStructOptions kDefaultMakeStructOptions; auto make_struct_function = std::make_shared<ScalarFunction>( @@ -176,8 +176,8 @@ void RegisterScalarNested(FunctionRegistry* registry) { kernel.mem_allocation = MemAllocation::NO_PREALLOCATE; DCHECK_OK(make_struct_function->AddKernel(std::move(kernel))); DCHECK_OK(registry->AddFunction(std::move(make_struct_function))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc index 867d8d041f..3e2e95e540 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_set_lookup.cc @@ -1,45 +1,45 @@ -// 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. - -#include "arrow/array/array_base.h" -#include "arrow/array/builder_primitive.h" -#include "arrow/compute/api_scalar.h" +// 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. + +#include "arrow/array/array_base.h" +#include "arrow/array/builder_primitive.h" +#include "arrow/compute/api_scalar.h" #include "arrow/compute/cast.h" -#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/common.h" #include "arrow/compute/kernels/util_internal.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_writer.h" -#include "arrow/util/hashing.h" -#include "arrow/visitor_inline.h" - -namespace arrow { - -using internal::checked_cast; -using internal::HashTraits; - -namespace compute { -namespace internal { -namespace { - -template <typename Type> -struct SetLookupState : public KernelState { +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_writer.h" +#include "arrow/util/hashing.h" +#include "arrow/visitor_inline.h" + +namespace arrow { + +using internal::checked_cast; +using internal::HashTraits; + +namespace compute { +namespace internal { +namespace { + +template <typename Type> +struct SetLookupState : public KernelState { explicit SetLookupState(MemoryPool* pool) : lookup_table(pool, 0) {} - - Status Init(const SetLookupOptions& options) { + + Status Init(const SetLookupOptions& options) { if (options.value_set.kind() == Datum::ARRAY) { const ArrayData& value_set = *options.value_set.array(); memo_index_to_value_index.reserve(value_set.length); @@ -63,11 +63,11 @@ struct SetLookupState : public KernelState { Status AddArrayValueSet(const SetLookupOptions& options, const ArrayData& data, int64_t start_index = 0) { - using T = typename GetViewType<Type>::T; + using T = typename GetViewType<Type>::T; int32_t index = static_cast<int32_t>(start_index); - auto visit_valid = [&](T v) { + auto visit_valid = [&](T v) { const auto memo_size = static_cast<int32_t>(memo_index_to_value_index.size()); - int32_t unused_memo_index; + int32_t unused_memo_index; auto on_found = [&](int32_t memo_index) { DCHECK_LT(memo_index, memo_size); }; auto on_not_found = [&](int32_t memo_index) { DCHECK_EQ(memo_index, memo_size); @@ -77,8 +77,8 @@ struct SetLookupState : public KernelState { v, std::move(on_found), std::move(on_not_found), &unused_memo_index)); ++index; return Status::OK(); - }; - auto visit_null = [&]() { + }; + auto visit_null = [&]() { const auto memo_size = static_cast<int32_t>(memo_index_to_value_index.size()); auto on_found = [&](int32_t memo_index) { DCHECK_LT(memo_index, memo_size); }; auto on_not_found = [&](int32_t memo_index) { @@ -87,96 +87,96 @@ struct SetLookupState : public KernelState { }; lookup_table.GetOrInsertNull(std::move(on_found), std::move(on_not_found)); ++index; - return Status::OK(); - }; + return Status::OK(); + }; return VisitArrayDataInline<Type>(data, visit_valid, visit_null); - } - - using MemoTable = typename HashTraits<Type>::MemoTableType; - MemoTable lookup_table; + } + + using MemoTable = typename HashTraits<Type>::MemoTableType; + MemoTable lookup_table; // When there are duplicates in value_set, the MemoTable indices must // be mapped back to indices in the value_set. std::vector<int32_t> memo_index_to_value_index; int32_t null_index = -1; -}; - -template <> -struct SetLookupState<NullType> : public KernelState { - explicit SetLookupState(MemoryPool*) {} - - Status Init(const SetLookupOptions& options) { +}; + +template <> +struct SetLookupState<NullType> : public KernelState { + explicit SetLookupState(MemoryPool*) {} + + Status Init(const SetLookupOptions& options) { value_set_has_null = (options.value_set.length() > 0) && !options.skip_nulls; - return Status::OK(); - } - + return Status::OK(); + } + bool value_set_has_null; -}; - -// TODO: Put this concept somewhere reusable -template <int width> -struct UnsignedIntType; - -template <> -struct UnsignedIntType<1> { - using Type = UInt8Type; -}; - -template <> -struct UnsignedIntType<2> { - using Type = UInt16Type; -}; - -template <> -struct UnsignedIntType<4> { - using Type = UInt32Type; -}; - -template <> -struct UnsignedIntType<8> { - using Type = UInt64Type; -}; - -// Constructing the type requires a type parameter -struct InitStateVisitor { - KernelContext* ctx; +}; + +// TODO: Put this concept somewhere reusable +template <int width> +struct UnsignedIntType; + +template <> +struct UnsignedIntType<1> { + using Type = UInt8Type; +}; + +template <> +struct UnsignedIntType<2> { + using Type = UInt16Type; +}; + +template <> +struct UnsignedIntType<4> { + using Type = UInt32Type; +}; + +template <> +struct UnsignedIntType<8> { + using Type = UInt64Type; +}; + +// Constructing the type requires a type parameter +struct InitStateVisitor { + KernelContext* ctx; SetLookupOptions options; const std::shared_ptr<DataType>& arg_type; - std::unique_ptr<KernelState> result; - + std::unique_ptr<KernelState> result; + InitStateVisitor(KernelContext* ctx, const KernelInitArgs& args) : ctx(ctx), options(*checked_cast<const SetLookupOptions*>(args.options)), arg_type(args.inputs[0].type) {} - - template <typename Type> - Status Init() { - using StateType = SetLookupState<Type>; - result.reset(new StateType(ctx->exec_context()->memory_pool())); + + template <typename Type> + Status Init() { + using StateType = SetLookupState<Type>; + result.reset(new StateType(ctx->exec_context()->memory_pool())); return static_cast<StateType*>(result.get())->Init(options); - } - - Status Visit(const DataType&) { return Init<NullType>(); } - - template <typename Type> - enable_if_boolean<Type, Status> Visit(const Type&) { - return Init<BooleanType>(); - } - - template <typename Type> - enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( - const Type&) { - return Init<typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); - } - - template <typename Type> - enable_if_base_binary<Type, Status> Visit(const Type&) { - return Init<typename Type::PhysicalType>(); - } - - // Handle Decimal128Type, FixedSizeBinaryType - Status Visit(const FixedSizeBinaryType& type) { return Init<FixedSizeBinaryType>(); } - + } + + Status Visit(const DataType&) { return Init<NullType>(); } + + template <typename Type> + enable_if_boolean<Type, Status> Visit(const Type&) { + return Init<BooleanType>(); + } + + template <typename Type> + enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( + const Type&) { + return Init<typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); + } + + template <typename Type> + enable_if_base_binary<Type, Status> Visit(const Type&) { + return Init<typename Type::PhysicalType>(); + } + + // Handle Decimal128Type, FixedSizeBinaryType + Status Visit(const FixedSizeBinaryType& type) { return Init<FixedSizeBinaryType>(); } + Result<std::unique_ptr<KernelState>> GetResult() { if (!options.value_set.type()->Equals(arg_type)) { ARROW_ASSIGN_OR_RAISE( @@ -186,9 +186,9 @@ struct InitStateVisitor { RETURN_NOT_OK(VisitTypeInline(*arg_type, this)); return std::move(result); - } -}; - + } +}; + Result<std::unique_ptr<KernelState>> InitSetLookup(KernelContext* ctx, const KernelInitArgs& args) { if (args.options == nullptr) { @@ -197,246 +197,246 @@ Result<std::unique_ptr<KernelState>> InitSetLookup(KernelContext* ctx, } return InitStateVisitor{ctx, args}.GetResult(); -} - -struct IndexInVisitor { - KernelContext* ctx; - const ArrayData& data; - Datum* out; - Int32Builder builder; - - IndexInVisitor(KernelContext* ctx, const ArrayData& data, Datum* out) - : ctx(ctx), data(data), out(out), builder(ctx->exec_context()->memory_pool()) {} - +} + +struct IndexInVisitor { + KernelContext* ctx; + const ArrayData& data; + Datum* out; + Int32Builder builder; + + IndexInVisitor(KernelContext* ctx, const ArrayData& data, Datum* out) + : ctx(ctx), data(data), out(out), builder(ctx->exec_context()->memory_pool()) {} + Status Visit(const DataType& type) { DCHECK_EQ(type.id(), Type::NA); - const auto& state = checked_cast<const SetLookupState<NullType>&>(*ctx->state()); - if (data.length != 0) { + const auto& state = checked_cast<const SetLookupState<NullType>&>(*ctx->state()); + if (data.length != 0) { // skip_nulls is honored for consistency with other types if (state.value_set_has_null) { - RETURN_NOT_OK(this->builder.Reserve(data.length)); - for (int64_t i = 0; i < data.length; ++i) { - this->builder.UnsafeAppend(0); - } + RETURN_NOT_OK(this->builder.Reserve(data.length)); + for (int64_t i = 0; i < data.length; ++i) { + this->builder.UnsafeAppend(0); + } } else { RETURN_NOT_OK(this->builder.AppendNulls(data.length)); - } - } - return Status::OK(); - } - - template <typename Type> - Status ProcessIndexIn() { - using T = typename GetViewType<Type>::T; - - const auto& state = checked_cast<const SetLookupState<Type>&>(*ctx->state()); - - RETURN_NOT_OK(this->builder.Reserve(data.length)); - VisitArrayDataInline<Type>( - data, - [&](T v) { - int32_t index = state.lookup_table.Get(v); - if (index != -1) { - // matching needle; output index from value_set + } + } + return Status::OK(); + } + + template <typename Type> + Status ProcessIndexIn() { + using T = typename GetViewType<Type>::T; + + const auto& state = checked_cast<const SetLookupState<Type>&>(*ctx->state()); + + RETURN_NOT_OK(this->builder.Reserve(data.length)); + VisitArrayDataInline<Type>( + data, + [&](T v) { + int32_t index = state.lookup_table.Get(v); + if (index != -1) { + // matching needle; output index from value_set this->builder.UnsafeAppend(state.memo_index_to_value_index[index]); - } else { - // no matching needle; output null - this->builder.UnsafeAppendNull(); - } - }, - [&]() { + } else { + // no matching needle; output null + this->builder.UnsafeAppendNull(); + } + }, + [&]() { if (state.null_index != -1) { - // value_set included null + // value_set included null this->builder.UnsafeAppend(state.null_index); - } else { - // value_set does not include null; output null - this->builder.UnsafeAppendNull(); - } - }); - return Status::OK(); - } - - template <typename Type> - enable_if_boolean<Type, Status> Visit(const Type&) { - return ProcessIndexIn<BooleanType>(); - } - - template <typename Type> - enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( - const Type&) { - return ProcessIndexIn< - typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); - } - - template <typename Type> - enable_if_base_binary<Type, Status> Visit(const Type&) { - return ProcessIndexIn<typename Type::PhysicalType>(); - } - - // Handle Decimal128Type, FixedSizeBinaryType - Status Visit(const FixedSizeBinaryType& type) { - return ProcessIndexIn<FixedSizeBinaryType>(); - } - - Status Execute() { - Status s = VisitTypeInline(*data.type, this); - if (!s.ok()) { - return s; - } - std::shared_ptr<ArrayData> out_data; - RETURN_NOT_OK(this->builder.FinishInternal(&out_data)); - out->value = std::move(out_data); - return Status::OK(); - } -}; - + } else { + // value_set does not include null; output null + this->builder.UnsafeAppendNull(); + } + }); + return Status::OK(); + } + + template <typename Type> + enable_if_boolean<Type, Status> Visit(const Type&) { + return ProcessIndexIn<BooleanType>(); + } + + template <typename Type> + enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( + const Type&) { + return ProcessIndexIn< + typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); + } + + template <typename Type> + enable_if_base_binary<Type, Status> Visit(const Type&) { + return ProcessIndexIn<typename Type::PhysicalType>(); + } + + // Handle Decimal128Type, FixedSizeBinaryType + Status Visit(const FixedSizeBinaryType& type) { + return ProcessIndexIn<FixedSizeBinaryType>(); + } + + Status Execute() { + Status s = VisitTypeInline(*data.type, this); + if (!s.ok()) { + return s; + } + std::shared_ptr<ArrayData> out_data; + RETURN_NOT_OK(this->builder.FinishInternal(&out_data)); + out->value = std::move(out_data); + return Status::OK(); + } +}; + Status ExecIndexIn(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return IndexInVisitor(ctx, *batch[0].array(), out).Execute(); -} - -// ---------------------------------------------------------------------- - +} + +// ---------------------------------------------------------------------- + // IsIn writes the results into a preallocated boolean data bitmap -struct IsInVisitor { - KernelContext* ctx; - const ArrayData& data; - Datum* out; - - IsInVisitor(KernelContext* ctx, const ArrayData& data, Datum* out) - : ctx(ctx), data(data), out(out) {} - +struct IsInVisitor { + KernelContext* ctx; + const ArrayData& data; + Datum* out; + + IsInVisitor(KernelContext* ctx, const ArrayData& data, Datum* out) + : ctx(ctx), data(data), out(out) {} + Status Visit(const DataType& type) { DCHECK_EQ(type.id(), Type::NA); - const auto& state = checked_cast<const SetLookupState<NullType>&>(*ctx->state()); - ArrayData* output = out->mutable_array(); + const auto& state = checked_cast<const SetLookupState<NullType>&>(*ctx->state()); + ArrayData* output = out->mutable_array(); // skip_nulls is honored for consistency with other types BitUtil::SetBitsTo(output->buffers[1]->mutable_data(), output->offset, output->length, state.value_set_has_null); - return Status::OK(); - } - - template <typename Type> - Status ProcessIsIn() { - using T = typename GetViewType<Type>::T; - const auto& state = checked_cast<const SetLookupState<Type>&>(*ctx->state()); - ArrayData* output = out->mutable_array(); - - FirstTimeBitmapWriter writer(output->buffers[1]->mutable_data(), output->offset, - output->length); - - VisitArrayDataInline<Type>( - this->data, - [&](T v) { - if (state.lookup_table.Get(v) != -1) { - writer.Set(); - } else { - writer.Clear(); - } - writer.Next(); - }, - [&]() { + return Status::OK(); + } + + template <typename Type> + Status ProcessIsIn() { + using T = typename GetViewType<Type>::T; + const auto& state = checked_cast<const SetLookupState<Type>&>(*ctx->state()); + ArrayData* output = out->mutable_array(); + + FirstTimeBitmapWriter writer(output->buffers[1]->mutable_data(), output->offset, + output->length); + + VisitArrayDataInline<Type>( + this->data, + [&](T v) { + if (state.lookup_table.Get(v) != -1) { + writer.Set(); + } else { + writer.Clear(); + } + writer.Next(); + }, + [&]() { if (state.null_index != -1) { writer.Set(); } else { writer.Clear(); } - writer.Next(); - }); - writer.Finish(); - return Status::OK(); - } - - template <typename Type> - enable_if_boolean<Type, Status> Visit(const Type&) { - return ProcessIsIn<BooleanType>(); - } - - template <typename Type> - enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( - const Type&) { - return ProcessIsIn<typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); - } - - template <typename Type> - enable_if_base_binary<Type, Status> Visit(const Type&) { - return ProcessIsIn<typename Type::PhysicalType>(); - } - - // Handle Decimal128Type, FixedSizeBinaryType - Status Visit(const FixedSizeBinaryType& type) { - return ProcessIsIn<FixedSizeBinaryType>(); - } - - Status Execute() { return VisitTypeInline(*data.type, this); } -}; - + writer.Next(); + }); + writer.Finish(); + return Status::OK(); + } + + template <typename Type> + enable_if_boolean<Type, Status> Visit(const Type&) { + return ProcessIsIn<BooleanType>(); + } + + template <typename Type> + enable_if_t<has_c_type<Type>::value && !is_boolean_type<Type>::value, Status> Visit( + const Type&) { + return ProcessIsIn<typename UnsignedIntType<sizeof(typename Type::c_type)>::Type>(); + } + + template <typename Type> + enable_if_base_binary<Type, Status> Visit(const Type&) { + return ProcessIsIn<typename Type::PhysicalType>(); + } + + // Handle Decimal128Type, FixedSizeBinaryType + Status Visit(const FixedSizeBinaryType& type) { + return ProcessIsIn<FixedSizeBinaryType>(); + } + + Status Execute() { return VisitTypeInline(*data.type, this); } +}; + Status ExecIsIn(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return IsInVisitor(ctx, *batch[0].array(), out).Execute(); -} - -// Unary set lookup kernels available for the following input types -// -// * Null type -// * Boolean -// * Numeric -// * Simple temporal types (date, time, timestamp) -// * Base binary types -// * Decimal - -void AddBasicSetLookupKernels(ScalarKernel kernel, - const std::shared_ptr<DataType>& out_ty, - ScalarFunction* func) { - auto AddKernels = [&](const std::vector<std::shared_ptr<DataType>>& types) { - for (const std::shared_ptr<DataType>& ty : types) { - kernel.signature = KernelSignature::Make({ty}, out_ty); - DCHECK_OK(func->AddKernel(kernel)); - } - }; - - AddKernels(BaseBinaryTypes()); - AddKernels(NumericTypes()); - AddKernels(TemporalTypes()); - - std::vector<Type::type> other_types = {Type::BOOL, Type::DECIMAL, - Type::FIXED_SIZE_BINARY}; - for (auto ty : other_types) { - kernel.signature = KernelSignature::Make({InputType::Array(ty)}, out_ty); - DCHECK_OK(func->AddKernel(kernel)); - } -} - -// Enables calling is_in with CallFunction as though it were binary. -class IsInMetaBinary : public MetaFunction { - public: +} + +// Unary set lookup kernels available for the following input types +// +// * Null type +// * Boolean +// * Numeric +// * Simple temporal types (date, time, timestamp) +// * Base binary types +// * Decimal + +void AddBasicSetLookupKernels(ScalarKernel kernel, + const std::shared_ptr<DataType>& out_ty, + ScalarFunction* func) { + auto AddKernels = [&](const std::vector<std::shared_ptr<DataType>>& types) { + for (const std::shared_ptr<DataType>& ty : types) { + kernel.signature = KernelSignature::Make({ty}, out_ty); + DCHECK_OK(func->AddKernel(kernel)); + } + }; + + AddKernels(BaseBinaryTypes()); + AddKernels(NumericTypes()); + AddKernels(TemporalTypes()); + + std::vector<Type::type> other_types = {Type::BOOL, Type::DECIMAL, + Type::FIXED_SIZE_BINARY}; + for (auto ty : other_types) { + kernel.signature = KernelSignature::Make({InputType::Array(ty)}, out_ty); + DCHECK_OK(func->AddKernel(kernel)); + } +} + +// Enables calling is_in with CallFunction as though it were binary. +class IsInMetaBinary : public MetaFunction { + public: IsInMetaBinary() : MetaFunction("is_in_meta_binary", Arity::Binary(), /*doc=*/nullptr) {} - - Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const override { - if (options != nullptr) { - return Status::Invalid("Unexpected options for 'is_in_meta_binary' function"); - } - return IsIn(args[0], args[1], ctx); - } -}; - -// Enables calling index_in with CallFunction as though it were binary. -class IndexInMetaBinary : public MetaFunction { - public: + + Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const override { + if (options != nullptr) { + return Status::Invalid("Unexpected options for 'is_in_meta_binary' function"); + } + return IsIn(args[0], args[1], ctx); + } +}; + +// Enables calling index_in with CallFunction as though it were binary. +class IndexInMetaBinary : public MetaFunction { + public: IndexInMetaBinary() : MetaFunction("index_in_meta_binary", Arity::Binary(), /*doc=*/nullptr) {} - - Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const override { - if (options != nullptr) { - return Status::Invalid("Unexpected options for 'index_in_meta_binary' function"); - } - return IndexIn(args[0], args[1], ctx); - } -}; - + + Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const override { + if (options != nullptr) { + return Status::Invalid("Unexpected options for 'index_in_meta_binary' function"); + } + return IndexIn(args[0], args[1], ctx); + } +}; + struct SetLookupFunction : ScalarFunction { using ScalarFunction::ScalarFunction; @@ -466,48 +466,48 @@ const FunctionDoc index_in_doc{ {"values"}, "SetLookupOptions"}; -} // namespace - -void RegisterScalarSetLookup(FunctionRegistry* registry) { +} // namespace + +void RegisterScalarSetLookup(FunctionRegistry* registry) { // IsIn writes its boolean output into preallocated memory - { - ScalarKernel isin_base; - isin_base.init = InitSetLookup; + { + ScalarKernel isin_base; + isin_base.init = InitSetLookup; isin_base.exec = TrivialScalarUnaryAsArraysExec(ExecIsIn, NullHandling::OUTPUT_NOT_NULL); isin_base.null_handling = NullHandling::OUTPUT_NOT_NULL; auto is_in = std::make_shared<SetLookupFunction>("is_in", Arity::Unary(), &is_in_doc); - - AddBasicSetLookupKernels(isin_base, /*output_type=*/boolean(), is_in.get()); - - isin_base.signature = KernelSignature::Make({null()}, boolean()); - DCHECK_OK(is_in->AddKernel(isin_base)); - DCHECK_OK(registry->AddFunction(is_in)); - - DCHECK_OK(registry->AddFunction(std::make_shared<IsInMetaBinary>())); - } - - // IndexIn uses Int32Builder and so is responsible for all its own allocation - { - ScalarKernel index_in_base; - index_in_base.init = InitSetLookup; + + AddBasicSetLookupKernels(isin_base, /*output_type=*/boolean(), is_in.get()); + + isin_base.signature = KernelSignature::Make({null()}, boolean()); + DCHECK_OK(is_in->AddKernel(isin_base)); + DCHECK_OK(registry->AddFunction(is_in)); + + DCHECK_OK(registry->AddFunction(std::make_shared<IsInMetaBinary>())); + } + + // IndexIn uses Int32Builder and so is responsible for all its own allocation + { + ScalarKernel index_in_base; + index_in_base.init = InitSetLookup; index_in_base.exec = TrivialScalarUnaryAsArraysExec( ExecIndexIn, NullHandling::COMPUTED_NO_PREALLOCATE); - index_in_base.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; - index_in_base.mem_allocation = MemAllocation::NO_PREALLOCATE; + index_in_base.null_handling = NullHandling::COMPUTED_NO_PREALLOCATE; + index_in_base.mem_allocation = MemAllocation::NO_PREALLOCATE; auto index_in = std::make_shared<SetLookupFunction>("index_in", Arity::Unary(), &index_in_doc); - - AddBasicSetLookupKernels(index_in_base, /*output_type=*/int32(), index_in.get()); - - index_in_base.signature = KernelSignature::Make({null()}, int32()); - DCHECK_OK(index_in->AddKernel(index_in_base)); - DCHECK_OK(registry->AddFunction(index_in)); - - DCHECK_OK(registry->AddFunction(std::make_shared<IndexInMetaBinary>())); - } -} - -} // namespace internal -} // namespace compute -} // namespace arrow + + AddBasicSetLookupKernels(index_in_base, /*output_type=*/int32(), index_in.get()); + + index_in_base.signature = KernelSignature::Make({null()}, int32()); + DCHECK_OK(index_in->AddKernel(index_in_base)); + DCHECK_OK(registry->AddFunction(index_in)); + + DCHECK_OK(registry->AddFunction(std::make_shared<IndexInMetaBinary>())); + } +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_string.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_string.cc index 9d2ed1764e..ab0a490eeb 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_string.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_string.cc @@ -1,29 +1,29 @@ -// 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. - -#include <algorithm> -#include <cctype> +// 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. + +#include <algorithm> +#include <cctype> #include <iterator> -#include <string> - -#ifdef ARROW_WITH_UTF8PROC -#include <utf8proc.h> -#endif - +#include <string> + +#ifdef ARROW_WITH_UTF8PROC +#include <utf8proc.h> +#endif + #ifdef ARROW_WITH_RE2 #include <re2/re2.h> #endif @@ -33,22 +33,22 @@ #include "arrow/buffer_builder.h" #include "arrow/builder.h" -#include "arrow/compute/api_scalar.h" -#include "arrow/compute/kernels/common.h" +#include "arrow/compute/api_scalar.h" +#include "arrow/compute/kernels/common.h" #include "arrow/util/checked_cast.h" -#include "arrow/util/utf8.h" -#include "arrow/util/value_parsing.h" +#include "arrow/util/utf8.h" +#include "arrow/util/value_parsing.h" #include "arrow/visitor_inline.h" - -namespace arrow { + +namespace arrow { using internal::checked_cast; -namespace compute { -namespace internal { - -namespace { - +namespace compute { +namespace internal { + +namespace { + #ifdef ARROW_WITH_RE2 util::string_view ToStringView(re2::StringPiece piece) { return {piece.data(), piece.length()}; @@ -66,33 +66,33 @@ Status RegexStatus(const RE2& regex) { } #endif -// Code units in the range [a-z] can only be an encoding of an ascii -// character/codepoint, not the 2nd, 3rd or 4th code unit (byte) of an different -// codepoint. This guaranteed by non-overlap design of the unicode standard. (see -// section 2.5 of Unicode Standard Core Specification v13.0) - -static inline uint8_t ascii_tolower(uint8_t utf8_code_unit) { - return ((utf8_code_unit >= 'A') && (utf8_code_unit <= 'Z')) ? (utf8_code_unit + 32) - : utf8_code_unit; -} - -static inline uint8_t ascii_toupper(uint8_t utf8_code_unit) { - return ((utf8_code_unit >= 'a') && (utf8_code_unit <= 'z')) ? (utf8_code_unit - 32) - : utf8_code_unit; -} - -template <typename T> -static inline bool IsAsciiCharacter(T character) { - return character < 128; -} - -struct BinaryLength { - template <typename OutValue, typename Arg0Value = util::string_view> +// Code units in the range [a-z] can only be an encoding of an ascii +// character/codepoint, not the 2nd, 3rd or 4th code unit (byte) of an different +// codepoint. This guaranteed by non-overlap design of the unicode standard. (see +// section 2.5 of Unicode Standard Core Specification v13.0) + +static inline uint8_t ascii_tolower(uint8_t utf8_code_unit) { + return ((utf8_code_unit >= 'A') && (utf8_code_unit <= 'Z')) ? (utf8_code_unit + 32) + : utf8_code_unit; +} + +static inline uint8_t ascii_toupper(uint8_t utf8_code_unit) { + return ((utf8_code_unit >= 'a') && (utf8_code_unit <= 'z')) ? (utf8_code_unit - 32) + : utf8_code_unit; +} + +template <typename T> +static inline bool IsAsciiCharacter(T character) { + return character < 128; +} + +struct BinaryLength { + template <typename OutValue, typename Arg0Value = util::string_view> static OutValue Call(KernelContext*, Arg0Value val, Status*) { - return static_cast<OutValue>(val.size()); - } -}; - + return static_cast<OutValue>(val.size()); + } +}; + struct Utf8Length { template <typename OutValue, typename Arg0Value = util::string_view> static OutValue Call(KernelContext*, Arg0Value val, Status*) { @@ -102,28 +102,28 @@ struct Utf8Length { } }; -#ifdef ARROW_WITH_UTF8PROC - -// Direct lookup tables for unicode properties -constexpr uint32_t kMaxCodepointLookup = - 0xffff; // up to this codepoint is in a lookup table -std::vector<uint32_t> lut_upper_codepoint; -std::vector<uint32_t> lut_lower_codepoint; -std::vector<utf8proc_category_t> lut_category; -std::once_flag flag_case_luts; - -void EnsureLookupTablesFilled() { - std::call_once(flag_case_luts, []() { - lut_upper_codepoint.reserve(kMaxCodepointLookup + 1); - lut_lower_codepoint.reserve(kMaxCodepointLookup + 1); - for (uint32_t i = 0; i <= kMaxCodepointLookup; i++) { - lut_upper_codepoint.push_back(utf8proc_toupper(i)); - lut_lower_codepoint.push_back(utf8proc_tolower(i)); - lut_category.push_back(utf8proc_category(i)); - } - }); -} - +#ifdef ARROW_WITH_UTF8PROC + +// Direct lookup tables for unicode properties +constexpr uint32_t kMaxCodepointLookup = + 0xffff; // up to this codepoint is in a lookup table +std::vector<uint32_t> lut_upper_codepoint; +std::vector<uint32_t> lut_lower_codepoint; +std::vector<utf8proc_category_t> lut_category; +std::once_flag flag_case_luts; + +void EnsureLookupTablesFilled() { + std::call_once(flag_case_luts, []() { + lut_upper_codepoint.reserve(kMaxCodepointLookup + 1); + lut_lower_codepoint.reserve(kMaxCodepointLookup + 1); + for (uint32_t i = 0; i <= kMaxCodepointLookup; i++) { + lut_upper_codepoint.push_back(utf8proc_toupper(i)); + lut_lower_codepoint.push_back(utf8proc_tolower(i)); + lut_category.push_back(utf8proc_category(i)); + } + }); +} + #else void EnsureLookupTablesFilled() {} @@ -154,67 +154,67 @@ struct StringTransformBase { template <typename Type, typename StringTransform> struct StringTransformExecBase { - using offset_type = typename Type::offset_type; - using ArrayType = typename TypeTraits<Type>::ArrayType; - + using offset_type = typename Type::offset_type; + using ArrayType = typename TypeTraits<Type>::ArrayType; + static Status Execute(KernelContext* ctx, StringTransform* transform, const ExecBatch& batch, Datum* out) { if (batch[0].kind() == Datum::ARRAY) { return ExecArray(ctx, transform, batch[0].array(), out); - } + } DCHECK_EQ(batch[0].kind(), Datum::SCALAR); return ExecScalar(ctx, transform, batch[0].scalar(), out); - } - + } + static Status ExecArray(KernelContext* ctx, StringTransform* transform, const std::shared_ptr<ArrayData>& data, Datum* out) { ArrayType input(data); ArrayData* output = out->mutable_array(); - + const int64_t input_ncodeunits = input.total_values_length(); const int64_t input_nstrings = input.length(); - + const int64_t output_ncodeunits_max = transform->MaxCodeunits(input_nstrings, input_ncodeunits); if (output_ncodeunits_max > std::numeric_limits<offset_type>::max()) { return Status::CapacityError( "Result might not fit in a 32bit utf8 array, convert to large_utf8"); } - + ARROW_ASSIGN_OR_RAISE(auto values_buffer, ctx->Allocate(output_ncodeunits_max)); output->buffers[2] = values_buffer; - + // String offsets are preallocated offset_type* output_string_offsets = output->GetMutableValues<offset_type>(1); uint8_t* output_str = output->buffers[2]->mutable_data(); offset_type output_ncodeunits = 0; - + output_string_offsets[0] = 0; for (int64_t i = 0; i < input_nstrings; i++) { if (!input.IsNull(i)) { - offset_type input_string_ncodeunits; + offset_type input_string_ncodeunits; const uint8_t* input_string = input.GetValue(i, &input_string_ncodeunits); auto encoded_nbytes = static_cast<offset_type>(transform->Transform( input_string, input_string_ncodeunits, output_str + output_ncodeunits)); if (encoded_nbytes < 0) { return transform->InvalidStatus(); - } - output_ncodeunits += encoded_nbytes; - } + } + output_ncodeunits += encoded_nbytes; + } output_string_offsets[i + 1] = output_ncodeunits; } DCHECK_LE(output_ncodeunits, output_ncodeunits_max); - + // Trim the codepoint buffer, since we allocated too much return values_buffer->Resize(output_ncodeunits, /*shrink_to_fit=*/true); } - + static Status ExecScalar(KernelContext* ctx, StringTransform* transform, const std::shared_ptr<Scalar>& scalar, Datum* out) { const auto& input = checked_cast<const BaseBinaryScalar&>(*scalar); if (!input.is_valid) { return Status::OK(); - } + } auto* result = checked_cast<BaseBinaryScalar*>(out->scalar().get()); result->is_valid = true; const int64_t data_nbytes = static_cast<int64_t>(input.value->size()); @@ -233,9 +233,9 @@ struct StringTransformExecBase { } DCHECK_LE(encoded_nbytes, output_ncodeunits_max); return value_buffer->Resize(encoded_nbytes, /*shrink_to_fit=*/true); - } -}; - + } +}; + template <typename Type, typename StringTransform> struct StringTransformExec : public StringTransformExecBase<Type, StringTransform> { using StringTransformExecBase<Type, StringTransform>::Execute; @@ -300,26 +300,26 @@ struct CaseMappingTransform { struct UTF8UpperTransform : public CaseMappingTransform { static uint32_t TransformCodepoint(uint32_t codepoint) { - return codepoint <= kMaxCodepointLookup ? lut_upper_codepoint[codepoint] - : utf8proc_toupper(codepoint); - } -}; - -template <typename Type> + return codepoint <= kMaxCodepointLookup ? lut_upper_codepoint[codepoint] + : utf8proc_toupper(codepoint); + } +}; + +template <typename Type> using UTF8Upper = StringTransformExec<Type, StringTransformCodepoint<UTF8UpperTransform>>; struct UTF8LowerTransform : public CaseMappingTransform { - static uint32_t TransformCodepoint(uint32_t codepoint) { - return codepoint <= kMaxCodepointLookup ? lut_lower_codepoint[codepoint] - : utf8proc_tolower(codepoint); - } -}; - + static uint32_t TransformCodepoint(uint32_t codepoint) { + return codepoint <= kMaxCodepointLookup ? lut_lower_codepoint[codepoint] + : utf8proc_tolower(codepoint); + } +}; + template <typename Type> using UTF8Lower = StringTransformExec<Type, StringTransformCodepoint<UTF8LowerTransform>>; - -#endif // ARROW_WITH_UTF8PROC - + +#endif // ARROW_WITH_UTF8PROC + struct AsciiReverseTransform : public StringTransformBase { int64_t Transform(const uint8_t* input, int64_t input_string_ncodeunits, uint8_t* output) { @@ -357,129 +357,129 @@ struct Utf8ReverseTransform : public StringTransformBase { template <typename Type> using Utf8Reverse = StringTransformExec<Type, Utf8ReverseTransform>; -using TransformFunc = std::function<void(const uint8_t*, int64_t, uint8_t*)>; - -// Transform a buffer of offsets to one which begins with 0 and has same -// value lengths. -template <typename T> -Status GetShiftedOffsets(KernelContext* ctx, const Buffer& input_buffer, int64_t offset, - int64_t length, std::shared_ptr<Buffer>* out) { - ARROW_ASSIGN_OR_RAISE(*out, ctx->Allocate((length + 1) * sizeof(T))); - const T* input_offsets = reinterpret_cast<const T*>(input_buffer.data()) + offset; - T* out_offsets = reinterpret_cast<T*>((*out)->mutable_data()); - T first_offset = *input_offsets; - for (int64_t i = 0; i < length; ++i) { - *out_offsets++ = input_offsets[i] - first_offset; - } - *out_offsets = input_offsets[length] - first_offset; - return Status::OK(); -} - -// Apply `transform` to input character data- this function cannot change the -// length -template <typename Type> +using TransformFunc = std::function<void(const uint8_t*, int64_t, uint8_t*)>; + +// Transform a buffer of offsets to one which begins with 0 and has same +// value lengths. +template <typename T> +Status GetShiftedOffsets(KernelContext* ctx, const Buffer& input_buffer, int64_t offset, + int64_t length, std::shared_ptr<Buffer>* out) { + ARROW_ASSIGN_OR_RAISE(*out, ctx->Allocate((length + 1) * sizeof(T))); + const T* input_offsets = reinterpret_cast<const T*>(input_buffer.data()) + offset; + T* out_offsets = reinterpret_cast<T*>((*out)->mutable_data()); + T first_offset = *input_offsets; + for (int64_t i = 0; i < length; ++i) { + *out_offsets++ = input_offsets[i] - first_offset; + } + *out_offsets = input_offsets[length] - first_offset; + return Status::OK(); +} + +// Apply `transform` to input character data- this function cannot change the +// length +template <typename Type> Status StringDataTransform(KernelContext* ctx, const ExecBatch& batch, TransformFunc transform, Datum* out) { - using ArrayType = typename TypeTraits<Type>::ArrayType; - using offset_type = typename Type::offset_type; - - if (batch[0].kind() == Datum::ARRAY) { - const ArrayData& input = *batch[0].array(); - ArrayType input_boxed(batch[0].array()); - - ArrayData* out_arr = out->mutable_array(); - - if (input.offset == 0) { - // We can reuse offsets from input - out_arr->buffers[1] = input.buffers[1]; - } else { - DCHECK(input.buffers[1]); - // We must allocate new space for the offsets and shift the existing offsets + using ArrayType = typename TypeTraits<Type>::ArrayType; + using offset_type = typename Type::offset_type; + + if (batch[0].kind() == Datum::ARRAY) { + const ArrayData& input = *batch[0].array(); + ArrayType input_boxed(batch[0].array()); + + ArrayData* out_arr = out->mutable_array(); + + if (input.offset == 0) { + // We can reuse offsets from input + out_arr->buffers[1] = input.buffers[1]; + } else { + DCHECK(input.buffers[1]); + // We must allocate new space for the offsets and shift the existing offsets RETURN_NOT_OK(GetShiftedOffsets<offset_type>(ctx, *input.buffers[1], input.offset, input.length, &out_arr->buffers[1])); - } - - // Allocate space for output data - int64_t data_nbytes = input_boxed.total_values_length(); + } + + // Allocate space for output data + int64_t data_nbytes = input_boxed.total_values_length(); RETURN_NOT_OK(ctx->Allocate(data_nbytes).Value(&out_arr->buffers[2])); - if (input.length > 0) { - transform(input.buffers[2]->data() + input_boxed.value_offset(0), data_nbytes, - out_arr->buffers[2]->mutable_data()); - } - } else { - const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); - auto result = checked_pointer_cast<BaseBinaryScalar>(MakeNullScalar(out->type())); - if (input.is_valid) { - result->is_valid = true; - int64_t data_nbytes = input.value->size(); + if (input.length > 0) { + transform(input.buffers[2]->data() + input_boxed.value_offset(0), data_nbytes, + out_arr->buffers[2]->mutable_data()); + } + } else { + const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); + auto result = checked_pointer_cast<BaseBinaryScalar>(MakeNullScalar(out->type())); + if (input.is_valid) { + result->is_valid = true; + int64_t data_nbytes = input.value->size(); RETURN_NOT_OK(ctx->Allocate(data_nbytes).Value(&result->value)); - transform(input.value->data(), data_nbytes, result->value->mutable_data()); - } + transform(input.value->data(), data_nbytes, result->value->mutable_data()); + } out->value = result; - } + } return Status::OK(); -} - -void TransformAsciiUpper(const uint8_t* input, int64_t length, uint8_t* output) { - std::transform(input, input + length, output, ascii_toupper); -} - -template <typename Type> -struct AsciiUpper { +} + +void TransformAsciiUpper(const uint8_t* input, int64_t length, uint8_t* output) { + std::transform(input, input + length, output, ascii_toupper); +} + +template <typename Type> +struct AsciiUpper { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return StringDataTransform<Type>(ctx, batch, TransformAsciiUpper, out); - } -}; - -void TransformAsciiLower(const uint8_t* input, int64_t length, uint8_t* output) { - std::transform(input, input + length, output, ascii_tolower); -} - -template <typename Type> -struct AsciiLower { + } +}; + +void TransformAsciiLower(const uint8_t* input, int64_t length, uint8_t* output) { + std::transform(input, input + length, output, ascii_tolower); +} + +template <typename Type> +struct AsciiLower { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { return StringDataTransform<Type>(ctx, batch, TransformAsciiLower, out); - } -}; - -// ---------------------------------------------------------------------- -// exact pattern detection - -using StrToBoolTransformFunc = - std::function<void(const void*, const uint8_t*, int64_t, int64_t, uint8_t*)>; - -// Apply `transform` to input character data- this function cannot change the -// length -template <typename Type> -void StringBoolTransform(KernelContext* ctx, const ExecBatch& batch, - StrToBoolTransformFunc transform, Datum* out) { - using offset_type = typename Type::offset_type; - - if (batch[0].kind() == Datum::ARRAY) { - const ArrayData& input = *batch[0].array(); - ArrayData* out_arr = out->mutable_array(); - if (input.length > 0) { - transform( - reinterpret_cast<const offset_type*>(input.buffers[1]->data()) + input.offset, - input.buffers[2]->data(), input.length, out_arr->offset, - out_arr->buffers[1]->mutable_data()); - } - } else { - const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); - if (input.is_valid) { - uint8_t result_value = 0; - std::array<offset_type, 2> offsets{0, - static_cast<offset_type>(input.value->size())}; - transform(offsets.data(), input.value->data(), 1, /*output_offset=*/0, - &result_value); + } +}; + +// ---------------------------------------------------------------------- +// exact pattern detection + +using StrToBoolTransformFunc = + std::function<void(const void*, const uint8_t*, int64_t, int64_t, uint8_t*)>; + +// Apply `transform` to input character data- this function cannot change the +// length +template <typename Type> +void StringBoolTransform(KernelContext* ctx, const ExecBatch& batch, + StrToBoolTransformFunc transform, Datum* out) { + using offset_type = typename Type::offset_type; + + if (batch[0].kind() == Datum::ARRAY) { + const ArrayData& input = *batch[0].array(); + ArrayData* out_arr = out->mutable_array(); + if (input.length > 0) { + transform( + reinterpret_cast<const offset_type*>(input.buffers[1]->data()) + input.offset, + input.buffers[2]->data(), input.length, out_arr->offset, + out_arr->buffers[1]->mutable_data()); + } + } else { + const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); + if (input.is_valid) { + uint8_t result_value = 0; + std::array<offset_type, 2> offsets{0, + static_cast<offset_type>(input.value->size())}; + transform(offsets.data(), input.value->data(), 1, /*output_offset=*/0, + &result_value); out->value = std::make_shared<BooleanScalar>(result_value > 0); - } - } -} - + } + } +} + using MatchSubstringState = OptionsWrapper<MatchSubstringOptions>; - + // This is an implementation of the Knuth-Morris-Pratt algorithm struct PlainSubstringMatcher { const MatchSubstringOptions& options_; @@ -507,31 +507,31 @@ struct PlainSubstringMatcher { } prefix_length++; prefix_table[pos + 1] = prefix_length; - } - } - + } + } + int64_t Find(util::string_view current) const { // Phase 2: Find the prefix in the data const auto pattern_length = options_.pattern.size(); - int64_t pattern_pos = 0; + int64_t pattern_pos = 0; int64_t pos = 0; if (pattern_length == 0) return 0; for (const auto c : current) { while ((pattern_pos >= 0) && (options_.pattern[pattern_pos] != c)) { - pattern_pos = prefix_table[pattern_pos]; - } - pattern_pos++; + pattern_pos = prefix_table[pattern_pos]; + } + pattern_pos++; if (static_cast<size_t>(pattern_pos) == pattern_length) { return pos + 1 - pattern_length; - } + } pos++; - } + } return -1; - } - + } + bool Match(util::string_view current) const { return Find(current) >= 0; } }; - + struct PlainStartsWithMatcher { const MatchSubstringOptions& options_; @@ -607,12 +607,12 @@ struct RegexSubstringMatcher { template <typename Type, typename Matcher> struct MatchSubstringImpl { - using offset_type = typename Type::offset_type; + using offset_type = typename Type::offset_type; static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out, const Matcher* matcher) { - StringBoolTransform<Type>( - ctx, batch, + StringBoolTransform<Type>( + ctx, batch, [&matcher](const void* raw_offsets, const uint8_t* data, int64_t length, int64_t output_offset, uint8_t* output) { const offset_type* offsets = reinterpret_cast<const offset_type*>(raw_offsets); @@ -626,12 +626,12 @@ struct MatchSubstringImpl { bitmap_writer.Next(); } bitmap_writer.Finish(); - }, - out); + }, + out); return Status::OK(); - } -}; - + } +}; + template <typename Type, typename Matcher> struct MatchSubstring { static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { @@ -842,7 +842,7 @@ const FunctionDoc match_like_doc( #endif -void AddMatchSubstring(FunctionRegistry* registry) { +void AddMatchSubstring(FunctionRegistry* registry) { { auto func = std::make_shared<ScalarFunction>("match_substring", Arity::Unary(), &match_substring_doc); @@ -1344,420 +1344,420 @@ void AddSlice(FunctionRegistry* registry) { &utf8_slice_codeunits_doc); using t32 = SliceCodeunits<StringType>; using t64 = SliceCodeunits<LargeStringType>; - DCHECK_OK( + DCHECK_OK( func->AddKernel({utf8()}, utf8(), t32::Exec, SliceCodeunitsTransform::State::Init)); DCHECK_OK(func->AddKernel({large_utf8()}, large_utf8(), t64::Exec, SliceCodeunitsTransform::State::Init)); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - -// IsAlpha/Digit etc - -#ifdef ARROW_WITH_UTF8PROC - -static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, uint32_t mask) { - utf8proc_category_t general_category = codepoint <= kMaxCodepointLookup - ? lut_category[codepoint] - : utf8proc_category(codepoint); - uint32_t general_category_bit = 1 << general_category; - // for e.g. undefined (but valid) codepoints, general_category == 0 == - // UTF8PROC_CATEGORY_CN - return (general_category != UTF8PROC_CATEGORY_CN) && - ((general_category_bit & mask) != 0); -} - -template <typename... Categories> -static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, uint32_t mask, - utf8proc_category_t category, - Categories... categories) { - return HasAnyUnicodeGeneralCategory(codepoint, mask | (1 << category), categories...); -} - -template <typename... Categories> -static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, - utf8proc_category_t category, - Categories... categories) { - return HasAnyUnicodeGeneralCategory(codepoint, static_cast<uint32_t>(1u << category), - categories...); -} - -static inline bool IsCasedCharacterUnicode(uint32_t codepoint) { - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU, - UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT) || - ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) != codepoint) || - (static_cast<uint32_t>(utf8proc_tolower(codepoint)) != codepoint)); -} - -static inline bool IsLowerCaseCharacterUnicode(uint32_t codepoint) { - // although this trick seems to work for upper case, this is not enough for lower case - // testing, see https://github.com/JuliaStrings/utf8proc/issues/195 . But currently the - // best we can do - return (HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LL) || - ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) != codepoint) && - (static_cast<uint32_t>(utf8proc_tolower(codepoint)) == codepoint))) && - !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LT); -} - -static inline bool IsUpperCaseCharacterUnicode(uint32_t codepoint) { - // this seems to be a good workaround for utf8proc not having case information - // https://github.com/JuliaStrings/utf8proc/issues/195 - return (HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU) || - ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) == codepoint) && - (static_cast<uint32_t>(utf8proc_tolower(codepoint)) != codepoint))) && - !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LT); -} - -static inline bool IsAlphaNumericCharacterUnicode(uint32_t codepoint) { - return HasAnyUnicodeGeneralCategory( - codepoint, UTF8PROC_CATEGORY_LU, UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT, - UTF8PROC_CATEGORY_LM, UTF8PROC_CATEGORY_LO, UTF8PROC_CATEGORY_ND, - UTF8PROC_CATEGORY_NL, UTF8PROC_CATEGORY_NO); -} - -static inline bool IsAlphaCharacterUnicode(uint32_t codepoint) { - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU, - UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT, - UTF8PROC_CATEGORY_LM, UTF8PROC_CATEGORY_LO); -} - -static inline bool IsDecimalCharacterUnicode(uint32_t codepoint) { - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND); -} - -static inline bool IsDigitCharacterUnicode(uint32_t codepoint) { - // Python defines this as Numeric_Type=Digit or Numeric_Type=Decimal. - // utf8proc has no support for this, this is the best we can do: - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND); -} - -static inline bool IsNumericCharacterUnicode(uint32_t codepoint) { - // Formally this is not correct, but utf8proc does not allow us to query for Numerical - // properties, e.g. Numeric_Value and Numeric_Type - // Python defines Numeric as Numeric_Type=Digit, Numeric_Type=Decimal or - // Numeric_Type=Numeric. - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND, - UTF8PROC_CATEGORY_NL, UTF8PROC_CATEGORY_NO); -} - -static inline bool IsSpaceCharacterUnicode(uint32_t codepoint) { - auto property = utf8proc_get_property(codepoint); - return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ZS) || - property->bidi_class == UTF8PROC_BIDI_CLASS_WS || - property->bidi_class == UTF8PROC_BIDI_CLASS_B || - property->bidi_class == UTF8PROC_BIDI_CLASS_S; -} - -static inline bool IsPrintableCharacterUnicode(uint32_t codepoint) { - uint32_t general_category = utf8proc_category(codepoint); - return (general_category != UTF8PROC_CATEGORY_CN) && - !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_CC, - UTF8PROC_CATEGORY_CF, UTF8PROC_CATEGORY_CS, - UTF8PROC_CATEGORY_CO, UTF8PROC_CATEGORY_ZS, - UTF8PROC_CATEGORY_ZL, UTF8PROC_CATEGORY_ZP); -} - -#endif - -static inline bool IsLowerCaseCharacterAscii(uint8_t ascii_character) { - return (ascii_character >= 'a') && (ascii_character <= 'z'); -} - -static inline bool IsUpperCaseCharacterAscii(uint8_t ascii_character) { - return (ascii_character >= 'A') && (ascii_character <= 'Z'); -} - -static inline bool IsCasedCharacterAscii(uint8_t ascii_character) { - return IsLowerCaseCharacterAscii(ascii_character) || - IsUpperCaseCharacterAscii(ascii_character); -} - -static inline bool IsAlphaCharacterAscii(uint8_t ascii_character) { - return IsCasedCharacterAscii(ascii_character); // same -} - -static inline bool IsAlphaNumericCharacterAscii(uint8_t ascii_character) { - return ((ascii_character >= '0') && (ascii_character <= '9')) || - ((ascii_character >= 'a') && (ascii_character <= 'z')) || - ((ascii_character >= 'A') && (ascii_character <= 'Z')); -} - -static inline bool IsDecimalCharacterAscii(uint8_t ascii_character) { - return ((ascii_character >= '0') && (ascii_character <= '9')); -} - -static inline bool IsSpaceCharacterAscii(uint8_t ascii_character) { - return ((ascii_character >= 0x09) && (ascii_character <= 0x0D)) || - (ascii_character == ' '); -} - -static inline bool IsPrintableCharacterAscii(uint8_t ascii_character) { - return ((ascii_character >= ' ') && (ascii_character <= '~')); -} - -template <typename Derived, bool allow_empty = false> -struct CharacterPredicateUnicode { + DCHECK_OK(registry->AddFunction(std::move(func))); +} + +// IsAlpha/Digit etc + +#ifdef ARROW_WITH_UTF8PROC + +static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, uint32_t mask) { + utf8proc_category_t general_category = codepoint <= kMaxCodepointLookup + ? lut_category[codepoint] + : utf8proc_category(codepoint); + uint32_t general_category_bit = 1 << general_category; + // for e.g. undefined (but valid) codepoints, general_category == 0 == + // UTF8PROC_CATEGORY_CN + return (general_category != UTF8PROC_CATEGORY_CN) && + ((general_category_bit & mask) != 0); +} + +template <typename... Categories> +static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, uint32_t mask, + utf8proc_category_t category, + Categories... categories) { + return HasAnyUnicodeGeneralCategory(codepoint, mask | (1 << category), categories...); +} + +template <typename... Categories> +static inline bool HasAnyUnicodeGeneralCategory(uint32_t codepoint, + utf8proc_category_t category, + Categories... categories) { + return HasAnyUnicodeGeneralCategory(codepoint, static_cast<uint32_t>(1u << category), + categories...); +} + +static inline bool IsCasedCharacterUnicode(uint32_t codepoint) { + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU, + UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT) || + ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) != codepoint) || + (static_cast<uint32_t>(utf8proc_tolower(codepoint)) != codepoint)); +} + +static inline bool IsLowerCaseCharacterUnicode(uint32_t codepoint) { + // although this trick seems to work for upper case, this is not enough for lower case + // testing, see https://github.com/JuliaStrings/utf8proc/issues/195 . But currently the + // best we can do + return (HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LL) || + ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) != codepoint) && + (static_cast<uint32_t>(utf8proc_tolower(codepoint)) == codepoint))) && + !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LT); +} + +static inline bool IsUpperCaseCharacterUnicode(uint32_t codepoint) { + // this seems to be a good workaround for utf8proc not having case information + // https://github.com/JuliaStrings/utf8proc/issues/195 + return (HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU) || + ((static_cast<uint32_t>(utf8proc_toupper(codepoint)) == codepoint) && + (static_cast<uint32_t>(utf8proc_tolower(codepoint)) != codepoint))) && + !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LT); +} + +static inline bool IsAlphaNumericCharacterUnicode(uint32_t codepoint) { + return HasAnyUnicodeGeneralCategory( + codepoint, UTF8PROC_CATEGORY_LU, UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT, + UTF8PROC_CATEGORY_LM, UTF8PROC_CATEGORY_LO, UTF8PROC_CATEGORY_ND, + UTF8PROC_CATEGORY_NL, UTF8PROC_CATEGORY_NO); +} + +static inline bool IsAlphaCharacterUnicode(uint32_t codepoint) { + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_LU, + UTF8PROC_CATEGORY_LL, UTF8PROC_CATEGORY_LT, + UTF8PROC_CATEGORY_LM, UTF8PROC_CATEGORY_LO); +} + +static inline bool IsDecimalCharacterUnicode(uint32_t codepoint) { + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND); +} + +static inline bool IsDigitCharacterUnicode(uint32_t codepoint) { + // Python defines this as Numeric_Type=Digit or Numeric_Type=Decimal. + // utf8proc has no support for this, this is the best we can do: + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND); +} + +static inline bool IsNumericCharacterUnicode(uint32_t codepoint) { + // Formally this is not correct, but utf8proc does not allow us to query for Numerical + // properties, e.g. Numeric_Value and Numeric_Type + // Python defines Numeric as Numeric_Type=Digit, Numeric_Type=Decimal or + // Numeric_Type=Numeric. + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ND, + UTF8PROC_CATEGORY_NL, UTF8PROC_CATEGORY_NO); +} + +static inline bool IsSpaceCharacterUnicode(uint32_t codepoint) { + auto property = utf8proc_get_property(codepoint); + return HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_ZS) || + property->bidi_class == UTF8PROC_BIDI_CLASS_WS || + property->bidi_class == UTF8PROC_BIDI_CLASS_B || + property->bidi_class == UTF8PROC_BIDI_CLASS_S; +} + +static inline bool IsPrintableCharacterUnicode(uint32_t codepoint) { + uint32_t general_category = utf8proc_category(codepoint); + return (general_category != UTF8PROC_CATEGORY_CN) && + !HasAnyUnicodeGeneralCategory(codepoint, UTF8PROC_CATEGORY_CC, + UTF8PROC_CATEGORY_CF, UTF8PROC_CATEGORY_CS, + UTF8PROC_CATEGORY_CO, UTF8PROC_CATEGORY_ZS, + UTF8PROC_CATEGORY_ZL, UTF8PROC_CATEGORY_ZP); +} + +#endif + +static inline bool IsLowerCaseCharacterAscii(uint8_t ascii_character) { + return (ascii_character >= 'a') && (ascii_character <= 'z'); +} + +static inline bool IsUpperCaseCharacterAscii(uint8_t ascii_character) { + return (ascii_character >= 'A') && (ascii_character <= 'Z'); +} + +static inline bool IsCasedCharacterAscii(uint8_t ascii_character) { + return IsLowerCaseCharacterAscii(ascii_character) || + IsUpperCaseCharacterAscii(ascii_character); +} + +static inline bool IsAlphaCharacterAscii(uint8_t ascii_character) { + return IsCasedCharacterAscii(ascii_character); // same +} + +static inline bool IsAlphaNumericCharacterAscii(uint8_t ascii_character) { + return ((ascii_character >= '0') && (ascii_character <= '9')) || + ((ascii_character >= 'a') && (ascii_character <= 'z')) || + ((ascii_character >= 'A') && (ascii_character <= 'Z')); +} + +static inline bool IsDecimalCharacterAscii(uint8_t ascii_character) { + return ((ascii_character >= '0') && (ascii_character <= '9')); +} + +static inline bool IsSpaceCharacterAscii(uint8_t ascii_character) { + return ((ascii_character >= 0x09) && (ascii_character <= 0x0D)) || + (ascii_character == ' '); +} + +static inline bool IsPrintableCharacterAscii(uint8_t ascii_character) { + return ((ascii_character >= ' ') && (ascii_character <= '~')); +} + +template <typename Derived, bool allow_empty = false> +struct CharacterPredicateUnicode { static bool Call(KernelContext*, const uint8_t* input, size_t input_string_ncodeunits, Status* st) { - if (allow_empty && input_string_ncodeunits == 0) { - return true; - } - bool all; - bool any = false; - if (!ARROW_PREDICT_TRUE(arrow::util::UTF8AllOf( - input, input + input_string_ncodeunits, &all, [&any](uint32_t codepoint) { - any |= Derived::PredicateCharacterAny(codepoint); - return Derived::PredicateCharacterAll(codepoint); - }))) { + if (allow_empty && input_string_ncodeunits == 0) { + return true; + } + bool all; + bool any = false; + if (!ARROW_PREDICT_TRUE(arrow::util::UTF8AllOf( + input, input + input_string_ncodeunits, &all, [&any](uint32_t codepoint) { + any |= Derived::PredicateCharacterAny(codepoint); + return Derived::PredicateCharacterAll(codepoint); + }))) { *st = Status::Invalid("Invalid UTF8 sequence in input"); - return false; - } - return all & any; - } - - static inline bool PredicateCharacterAny(uint32_t) { - return true; // default condition make sure there is at least 1 charachter - } -}; - -template <typename Derived, bool allow_empty = false> -struct CharacterPredicateAscii { + return false; + } + return all & any; + } + + static inline bool PredicateCharacterAny(uint32_t) { + return true; // default condition make sure there is at least 1 charachter + } +}; + +template <typename Derived, bool allow_empty = false> +struct CharacterPredicateAscii { static bool Call(KernelContext*, const uint8_t* input, size_t input_string_ncodeunits, Status*) { - if (allow_empty && input_string_ncodeunits == 0) { - return true; - } - bool any = false; - // MB: A simple for loops seems 8% faster on gcc 9.3, running the IsAlphaNumericAscii - // benchmark. I don't consider that worth it. - bool all = std::all_of(input, input + input_string_ncodeunits, - [&any](uint8_t ascii_character) { - any |= Derived::PredicateCharacterAny(ascii_character); - return Derived::PredicateCharacterAll(ascii_character); - }); - return all & any; - } - - static inline bool PredicateCharacterAny(uint8_t) { - return true; // default condition make sure there is at least 1 charachter - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsAlphaNumericUnicode : CharacterPredicateUnicode<IsAlphaNumericUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsAlphaNumericCharacterUnicode(codepoint); - } -}; -#endif - -struct IsAlphaNumericAscii : CharacterPredicateAscii<IsAlphaNumericAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - return IsAlphaNumericCharacterAscii(ascii_character); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsAlphaUnicode : CharacterPredicateUnicode<IsAlphaUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsAlphaCharacterUnicode(codepoint); - } -}; -#endif - -struct IsAlphaAscii : CharacterPredicateAscii<IsAlphaAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - return IsAlphaCharacterAscii(ascii_character); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsDecimalUnicode : CharacterPredicateUnicode<IsDecimalUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsDecimalCharacterUnicode(codepoint); - } -}; -#endif - -struct IsDecimalAscii : CharacterPredicateAscii<IsDecimalAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - return IsDecimalCharacterAscii(ascii_character); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsDigitUnicode : CharacterPredicateUnicode<IsDigitUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsDigitCharacterUnicode(codepoint); - } -}; - -struct IsNumericUnicode : CharacterPredicateUnicode<IsNumericUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsNumericCharacterUnicode(codepoint); - } -}; -#endif - -struct IsAscii { + if (allow_empty && input_string_ncodeunits == 0) { + return true; + } + bool any = false; + // MB: A simple for loops seems 8% faster on gcc 9.3, running the IsAlphaNumericAscii + // benchmark. I don't consider that worth it. + bool all = std::all_of(input, input + input_string_ncodeunits, + [&any](uint8_t ascii_character) { + any |= Derived::PredicateCharacterAny(ascii_character); + return Derived::PredicateCharacterAll(ascii_character); + }); + return all & any; + } + + static inline bool PredicateCharacterAny(uint8_t) { + return true; // default condition make sure there is at least 1 charachter + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsAlphaNumericUnicode : CharacterPredicateUnicode<IsAlphaNumericUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsAlphaNumericCharacterUnicode(codepoint); + } +}; +#endif + +struct IsAlphaNumericAscii : CharacterPredicateAscii<IsAlphaNumericAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + return IsAlphaNumericCharacterAscii(ascii_character); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsAlphaUnicode : CharacterPredicateUnicode<IsAlphaUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsAlphaCharacterUnicode(codepoint); + } +}; +#endif + +struct IsAlphaAscii : CharacterPredicateAscii<IsAlphaAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + return IsAlphaCharacterAscii(ascii_character); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsDecimalUnicode : CharacterPredicateUnicode<IsDecimalUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsDecimalCharacterUnicode(codepoint); + } +}; +#endif + +struct IsDecimalAscii : CharacterPredicateAscii<IsDecimalAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + return IsDecimalCharacterAscii(ascii_character); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsDigitUnicode : CharacterPredicateUnicode<IsDigitUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsDigitCharacterUnicode(codepoint); + } +}; + +struct IsNumericUnicode : CharacterPredicateUnicode<IsNumericUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsNumericCharacterUnicode(codepoint); + } +}; +#endif + +struct IsAscii { static bool Call(KernelContext*, const uint8_t* input, size_t input_string_nascii_characters, Status*) { - return std::all_of(input, input + input_string_nascii_characters, - IsAsciiCharacter<uint8_t>); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsLowerUnicode : CharacterPredicateUnicode<IsLowerUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - // Only for cased character it needs to be lower case - return !IsCasedCharacterUnicode(codepoint) || IsLowerCaseCharacterUnicode(codepoint); - } - static inline bool PredicateCharacterAny(uint32_t codepoint) { - return IsCasedCharacterUnicode(codepoint); // at least 1 cased character - } -}; -#endif - -struct IsLowerAscii : CharacterPredicateAscii<IsLowerAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - // Only for cased character it needs to be lower case - return !IsCasedCharacterAscii(ascii_character) || - IsLowerCaseCharacterAscii(ascii_character); - } - static inline bool PredicateCharacterAny(uint8_t ascii_character) { - return IsCasedCharacterAscii(ascii_character); // at least 1 cased character - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsPrintableUnicode - : CharacterPredicateUnicode<IsPrintableUnicode, /*allow_empty=*/true> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return codepoint == ' ' || IsPrintableCharacterUnicode(codepoint); - } -}; -#endif - -struct IsPrintableAscii - : CharacterPredicateAscii<IsPrintableAscii, /*allow_empty=*/true> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - return IsPrintableCharacterAscii(ascii_character); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsSpaceUnicode : CharacterPredicateUnicode<IsSpaceUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - return IsSpaceCharacterUnicode(codepoint); - } -}; -#endif - -struct IsSpaceAscii : CharacterPredicateAscii<IsSpaceAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - return IsSpaceCharacterAscii(ascii_character); - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsTitleUnicode { + return std::all_of(input, input + input_string_nascii_characters, + IsAsciiCharacter<uint8_t>); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsLowerUnicode : CharacterPredicateUnicode<IsLowerUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + // Only for cased character it needs to be lower case + return !IsCasedCharacterUnicode(codepoint) || IsLowerCaseCharacterUnicode(codepoint); + } + static inline bool PredicateCharacterAny(uint32_t codepoint) { + return IsCasedCharacterUnicode(codepoint); // at least 1 cased character + } +}; +#endif + +struct IsLowerAscii : CharacterPredicateAscii<IsLowerAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + // Only for cased character it needs to be lower case + return !IsCasedCharacterAscii(ascii_character) || + IsLowerCaseCharacterAscii(ascii_character); + } + static inline bool PredicateCharacterAny(uint8_t ascii_character) { + return IsCasedCharacterAscii(ascii_character); // at least 1 cased character + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsPrintableUnicode + : CharacterPredicateUnicode<IsPrintableUnicode, /*allow_empty=*/true> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return codepoint == ' ' || IsPrintableCharacterUnicode(codepoint); + } +}; +#endif + +struct IsPrintableAscii + : CharacterPredicateAscii<IsPrintableAscii, /*allow_empty=*/true> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + return IsPrintableCharacterAscii(ascii_character); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsSpaceUnicode : CharacterPredicateUnicode<IsSpaceUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + return IsSpaceCharacterUnicode(codepoint); + } +}; +#endif + +struct IsSpaceAscii : CharacterPredicateAscii<IsSpaceAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + return IsSpaceCharacterAscii(ascii_character); + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsTitleUnicode { static bool Call(KernelContext*, const uint8_t* input, size_t input_string_ncodeunits, Status* st) { - // rules: - // * 1: lower case follows cased - // * 2: upper case follows uncased - // * 3: at least 1 cased character (which logically should be upper/title) - bool rules_1_and_2; - bool previous_cased = false; // in LL, LU or LT - bool rule_3 = false; - bool status = - arrow::util::UTF8AllOf(input, input + input_string_ncodeunits, &rules_1_and_2, - [&previous_cased, &rule_3](uint32_t codepoint) { - if (IsLowerCaseCharacterUnicode(codepoint)) { - if (!previous_cased) return false; // rule 1 broken - previous_cased = true; - } else if (IsCasedCharacterUnicode(codepoint)) { - if (previous_cased) return false; // rule 2 broken - // next should be a lower case or uncased - previous_cased = true; - rule_3 = true; // rule 3 obeyed - } else { - // a non-cased char, like _ or 1 - // next should be upper case or more uncased - previous_cased = false; - } - return true; - }); - if (!ARROW_PREDICT_TRUE(status)) { + // rules: + // * 1: lower case follows cased + // * 2: upper case follows uncased + // * 3: at least 1 cased character (which logically should be upper/title) + bool rules_1_and_2; + bool previous_cased = false; // in LL, LU or LT + bool rule_3 = false; + bool status = + arrow::util::UTF8AllOf(input, input + input_string_ncodeunits, &rules_1_and_2, + [&previous_cased, &rule_3](uint32_t codepoint) { + if (IsLowerCaseCharacterUnicode(codepoint)) { + if (!previous_cased) return false; // rule 1 broken + previous_cased = true; + } else if (IsCasedCharacterUnicode(codepoint)) { + if (previous_cased) return false; // rule 2 broken + // next should be a lower case or uncased + previous_cased = true; + rule_3 = true; // rule 3 obeyed + } else { + // a non-cased char, like _ or 1 + // next should be upper case or more uncased + previous_cased = false; + } + return true; + }); + if (!ARROW_PREDICT_TRUE(status)) { *st = Status::Invalid("Invalid UTF8 sequence in input"); - return false; - } - return rules_1_and_2 & rule_3; - } -}; -#endif - -struct IsTitleAscii { + return false; + } + return rules_1_and_2 & rule_3; + } +}; +#endif + +struct IsTitleAscii { static bool Call(KernelContext*, const uint8_t* input, size_t input_string_ncodeunits, Status*) { - // rules: - // * 1: lower case follows cased - // * 2: upper case follows uncased - // * 3: at least 1 cased character (which logically should be upper/title) - bool rules_1_and_2 = true; - bool previous_cased = false; // in LL, LU or LT - bool rule_3 = false; - // we cannot rely on std::all_of because we need guaranteed order - for (const uint8_t* c = input; c < input + input_string_ncodeunits; ++c) { - if (IsLowerCaseCharacterAscii(*c)) { - if (!previous_cased) { - // rule 1 broken - rules_1_and_2 = false; - break; - } - previous_cased = true; - } else if (IsCasedCharacterAscii(*c)) { - if (previous_cased) { - // rule 2 broken - rules_1_and_2 = false; - break; - } - // next should be a lower case or uncased - previous_cased = true; - rule_3 = true; // rule 3 obeyed - } else { - // a non-cased char, like _ or 1 - // next should be upper case or more uncased - previous_cased = false; - } - } - return rules_1_and_2 & rule_3; - } -}; - -#ifdef ARROW_WITH_UTF8PROC -struct IsUpperUnicode : CharacterPredicateUnicode<IsUpperUnicode> { - static inline bool PredicateCharacterAll(uint32_t codepoint) { - // Only for cased character it needs to be lower case - return !IsCasedCharacterUnicode(codepoint) || IsUpperCaseCharacterUnicode(codepoint); - } - static inline bool PredicateCharacterAny(uint32_t codepoint) { - return IsCasedCharacterUnicode(codepoint); // at least 1 cased character - } -}; -#endif - -struct IsUpperAscii : CharacterPredicateAscii<IsUpperAscii> { - static inline bool PredicateCharacterAll(uint8_t ascii_character) { - // Only for cased character it needs to be lower case - return !IsCasedCharacterAscii(ascii_character) || - IsUpperCaseCharacterAscii(ascii_character); - } - static inline bool PredicateCharacterAny(uint8_t ascii_character) { - return IsCasedCharacterAscii(ascii_character); // at least 1 cased character - } -}; - + // rules: + // * 1: lower case follows cased + // * 2: upper case follows uncased + // * 3: at least 1 cased character (which logically should be upper/title) + bool rules_1_and_2 = true; + bool previous_cased = false; // in LL, LU or LT + bool rule_3 = false; + // we cannot rely on std::all_of because we need guaranteed order + for (const uint8_t* c = input; c < input + input_string_ncodeunits; ++c) { + if (IsLowerCaseCharacterAscii(*c)) { + if (!previous_cased) { + // rule 1 broken + rules_1_and_2 = false; + break; + } + previous_cased = true; + } else if (IsCasedCharacterAscii(*c)) { + if (previous_cased) { + // rule 2 broken + rules_1_and_2 = false; + break; + } + // next should be a lower case or uncased + previous_cased = true; + rule_3 = true; // rule 3 obeyed + } else { + // a non-cased char, like _ or 1 + // next should be upper case or more uncased + previous_cased = false; + } + } + return rules_1_and_2 & rule_3; + } +}; + +#ifdef ARROW_WITH_UTF8PROC +struct IsUpperUnicode : CharacterPredicateUnicode<IsUpperUnicode> { + static inline bool PredicateCharacterAll(uint32_t codepoint) { + // Only for cased character it needs to be lower case + return !IsCasedCharacterUnicode(codepoint) || IsUpperCaseCharacterUnicode(codepoint); + } + static inline bool PredicateCharacterAny(uint32_t codepoint) { + return IsCasedCharacterUnicode(codepoint); // at least 1 cased character + } +}; +#endif + +struct IsUpperAscii : CharacterPredicateAscii<IsUpperAscii> { + static inline bool PredicateCharacterAll(uint8_t ascii_character) { + // Only for cased character it needs to be lower case + return !IsCasedCharacterAscii(ascii_character) || + IsUpperCaseCharacterAscii(ascii_character); + } + static inline bool PredicateCharacterAny(uint8_t ascii_character) { + return IsCasedCharacterAscii(ascii_character); // at least 1 cased character + } +}; + // splitting template <typename Options> @@ -2215,7 +2215,7 @@ void AddSplit(FunctionRegistry* registry) { #endif } -// ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- // Replace substring (plain, regex) template <typename Type, typename Replacer> @@ -2773,43 +2773,43 @@ void AddExtractRegex(FunctionRegistry* registry) { #endif // ARROW_WITH_RE2 // ---------------------------------------------------------------------- -// strptime string parsing - -using StrptimeState = OptionsWrapper<StrptimeOptions>; - -struct ParseStrptime { - explicit ParseStrptime(const StrptimeOptions& options) - : parser(TimestampParser::MakeStrptime(options.format)), unit(options.unit) {} - - template <typename... Ignored> +// strptime string parsing + +using StrptimeState = OptionsWrapper<StrptimeOptions>; + +struct ParseStrptime { + explicit ParseStrptime(const StrptimeOptions& options) + : parser(TimestampParser::MakeStrptime(options.format)), unit(options.unit) {} + + template <typename... Ignored> int64_t Call(KernelContext*, util::string_view val, Status* st) const { - int64_t result = 0; - if (!(*parser)(val.data(), val.size(), unit, &result)) { + int64_t result = 0; + if (!(*parser)(val.data(), val.size(), unit, &result)) { *st = Status::Invalid("Failed to parse string: '", val, "' as a scalar of type ", TimestampType(unit).ToString()); - } - return result; - } - - std::shared_ptr<TimestampParser> parser; - TimeUnit::type unit; -}; - -template <typename InputType> + } + return result; + } + + std::shared_ptr<TimestampParser> parser; + TimeUnit::type unit; +}; + +template <typename InputType> Status StrptimeExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - applicator::ScalarUnaryNotNullStateful<TimestampType, InputType, ParseStrptime> kernel{ - ParseStrptime(StrptimeState::Get(ctx))}; - return kernel.Exec(ctx, batch, out); -} - -Result<ValueDescr> StrptimeResolve(KernelContext* ctx, const std::vector<ValueDescr>&) { - if (ctx->state()) { - return ::arrow::timestamp(StrptimeState::Get(ctx).unit); - } - - return Status::Invalid("strptime does not provide default StrptimeOptions"); -} - + applicator::ScalarUnaryNotNullStateful<TimestampType, InputType, ParseStrptime> kernel{ + ParseStrptime(StrptimeState::Get(ctx))}; + return kernel.Exec(ctx, batch, out); +} + +Result<ValueDescr> StrptimeResolve(KernelContext* ctx, const std::vector<ValueDescr>&) { + if (ctx->state()) { + return ::arrow::timestamp(StrptimeState::Get(ctx).unit); + } + + return Status::Invalid("strptime does not provide default StrptimeOptions"); +} + // ---------------------------------------------------------------------- // string padding @@ -3273,31 +3273,31 @@ const FunctionDoc utf8_length_doc("Compute UTF8 string lengths", "UTF8 characters. Null values emit null."), {"strings"}); -void AddStrptime(FunctionRegistry* registry) { +void AddStrptime(FunctionRegistry* registry) { auto func = std::make_shared<ScalarFunction>("strptime", Arity::Unary(), &strptime_doc); - DCHECK_OK(func->AddKernel({utf8()}, OutputType(StrptimeResolve), - StrptimeExec<StringType>, StrptimeState::Init)); - DCHECK_OK(func->AddKernel({large_utf8()}, OutputType(StrptimeResolve), - StrptimeExec<LargeStringType>, StrptimeState::Init)); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - -void AddBinaryLength(FunctionRegistry* registry) { + DCHECK_OK(func->AddKernel({utf8()}, OutputType(StrptimeResolve), + StrptimeExec<StringType>, StrptimeState::Init)); + DCHECK_OK(func->AddKernel({large_utf8()}, OutputType(StrptimeResolve), + StrptimeExec<LargeStringType>, StrptimeState::Init)); + DCHECK_OK(registry->AddFunction(std::move(func))); +} + +void AddBinaryLength(FunctionRegistry* registry) { auto func = std::make_shared<ScalarFunction>("binary_length", Arity::Unary(), &binary_length_doc); - ArrayKernelExec exec_offset_32 = - applicator::ScalarUnaryNotNull<Int32Type, StringType, BinaryLength>::Exec; - ArrayKernelExec exec_offset_64 = - applicator::ScalarUnaryNotNull<Int64Type, LargeStringType, BinaryLength>::Exec; - for (const auto& input_type : {binary(), utf8()}) { - DCHECK_OK(func->AddKernel({input_type}, int32(), exec_offset_32)); - } - for (const auto& input_type : {large_binary(), large_utf8()}) { - DCHECK_OK(func->AddKernel({input_type}, int64(), exec_offset_64)); - } - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + ArrayKernelExec exec_offset_32 = + applicator::ScalarUnaryNotNull<Int32Type, StringType, BinaryLength>::Exec; + ArrayKernelExec exec_offset_64 = + applicator::ScalarUnaryNotNull<Int64Type, LargeStringType, BinaryLength>::Exec; + for (const auto& input_type : {binary(), utf8()}) { + DCHECK_OK(func->AddKernel({input_type}, int32(), exec_offset_32)); + } + for (const auto& input_type : {large_binary(), large_utf8()}) { + DCHECK_OK(func->AddKernel({input_type}, int64(), exec_offset_64)); + } + DCHECK_OK(registry->AddFunction(std::move(func))); +} + void AddUtf8Length(FunctionRegistry* registry) { auto func = std::make_shared<ScalarFunction>("utf8_length", Arity::Unary(), &utf8_length_doc); @@ -3821,7 +3821,7 @@ void AddBinaryJoin(FunctionRegistry* registry) { } } -template <template <typename> class ExecFunctor> +template <template <typename> class ExecFunctor> void MakeUnaryStringBatchKernel( std::string name, FunctionRegistry* registry, const FunctionDoc* doc, MemAllocation::type mem_allocation = MemAllocation::PREALLOCATE) { @@ -3838,9 +3838,9 @@ void MakeUnaryStringBatchKernel( kernel.mem_allocation = mem_allocation; DCHECK_OK(func->AddKernel(std::move(kernel))); } - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + DCHECK_OK(registry->AddFunction(std::move(func))); +} + template <template <typename> class ExecFunctor> void MakeUnaryStringBatchKernelWithState( std::string name, FunctionRegistry* registry, const FunctionDoc* doc, @@ -3861,71 +3861,71 @@ void MakeUnaryStringBatchKernelWithState( DCHECK_OK(registry->AddFunction(std::move(func))); } -#ifdef ARROW_WITH_UTF8PROC - -template <template <typename> class Transformer> +#ifdef ARROW_WITH_UTF8PROC + +template <template <typename> class Transformer> void MakeUnaryStringUTF8TransformKernel(std::string name, FunctionRegistry* registry, const FunctionDoc* doc) { auto func = std::make_shared<ScalarFunction>(name, Arity::Unary(), doc); - ArrayKernelExec exec_32 = Transformer<StringType>::Exec; - ArrayKernelExec exec_64 = Transformer<LargeStringType>::Exec; - DCHECK_OK(func->AddKernel({utf8()}, utf8(), exec_32)); - DCHECK_OK(func->AddKernel({large_utf8()}, large_utf8(), exec_64)); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - -#endif - + ArrayKernelExec exec_32 = Transformer<StringType>::Exec; + ArrayKernelExec exec_64 = Transformer<LargeStringType>::Exec; + DCHECK_OK(func->AddKernel({utf8()}, utf8(), exec_32)); + DCHECK_OK(func->AddKernel({large_utf8()}, large_utf8(), exec_64)); + DCHECK_OK(registry->AddFunction(std::move(func))); +} + +#endif + // NOTE: Predicate should only populate 'status' with errors, // leave it unmodified to indicate Status::OK() using StringPredicate = std::function<bool(KernelContext*, const uint8_t*, size_t, Status*)>; - -template <typename Type> + +template <typename Type> Status ApplyPredicate(KernelContext* ctx, const ExecBatch& batch, StringPredicate predicate, Datum* out) { Status st = Status::OK(); - EnsureLookupTablesFilled(); - if (batch[0].kind() == Datum::ARRAY) { - const ArrayData& input = *batch[0].array(); - ArrayIterator<Type> input_it(input); - ArrayData* out_arr = out->mutable_array(); - ::arrow::internal::GenerateBitsUnrolled( - out_arr->buffers[1]->mutable_data(), out_arr->offset, input.length, - [&]() -> bool { - util::string_view val = input_it(); + EnsureLookupTablesFilled(); + if (batch[0].kind() == Datum::ARRAY) { + const ArrayData& input = *batch[0].array(); + ArrayIterator<Type> input_it(input); + ArrayData* out_arr = out->mutable_array(); + ::arrow::internal::GenerateBitsUnrolled( + out_arr->buffers[1]->mutable_data(), out_arr->offset, input.length, + [&]() -> bool { + util::string_view val = input_it(); return predicate(ctx, reinterpret_cast<const uint8_t*>(val.data()), val.size(), &st); - }); - } else { - const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); - if (input.is_valid) { + }); + } else { + const auto& input = checked_cast<const BaseBinaryScalar&>(*batch[0].scalar()); + if (input.is_valid) { bool boolean_result = predicate(ctx, input.value->data(), static_cast<size_t>(input.value->size()), &st); // UTF decoding can lead to issues if (st.ok()) { out->value = std::make_shared<BooleanScalar>(boolean_result); - } - } - } + } + } + } return st; -} - -template <typename Predicate> +} + +template <typename Predicate> void AddUnaryStringPredicate(std::string name, FunctionRegistry* registry, const FunctionDoc* doc) { auto func = std::make_shared<ScalarFunction>(name, Arity::Unary(), doc); - auto exec_32 = [](KernelContext* ctx, const ExecBatch& batch, Datum* out) { + auto exec_32 = [](KernelContext* ctx, const ExecBatch& batch, Datum* out) { return ApplyPredicate<StringType>(ctx, batch, Predicate::Call, out); - }; - auto exec_64 = [](KernelContext* ctx, const ExecBatch& batch, Datum* out) { + }; + auto exec_64 = [](KernelContext* ctx, const ExecBatch& batch, Datum* out) { return ApplyPredicate<LargeStringType>(ctx, batch, Predicate::Call, out); - }; - DCHECK_OK(func->AddKernel({utf8()}, boolean(), std::move(exec_32))); - DCHECK_OK(func->AddKernel({large_utf8()}, boolean(), std::move(exec_64))); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + }; + DCHECK_OK(func->AddKernel({utf8()}, boolean(), std::move(exec_32))); + DCHECK_OK(func->AddKernel({large_utf8()}, boolean(), std::move(exec_64))); + DCHECK_OK(registry->AddFunction(std::move(func))); +} + FunctionDoc StringPredicateDoc(std::string summary, std::string description) { return FunctionDoc{std::move(summary), std::move(description), {"strings"}}; } @@ -4041,9 +4041,9 @@ const FunctionDoc utf8_reverse_doc( "composed of multiple codepoints."), {"strings"}); -} // namespace - -void RegisterScalarStringAscii(FunctionRegistry* registry) { +} // namespace + +void RegisterScalarStringAscii(FunctionRegistry* registry) { // ascii_upper and ascii_lower are able to reuse the original offsets buffer, // so don't preallocate them in the output. MakeUnaryStringBatchKernel<AsciiUpper>("ascii_upper", registry, &ascii_upper_doc, @@ -4058,7 +4058,7 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { &ascii_rtrim_whitespace_doc); MakeUnaryStringBatchKernel<AsciiReverse>("ascii_reverse", registry, &ascii_reverse_doc); MakeUnaryStringBatchKernel<Utf8Reverse>("utf8_reverse", registry, &utf8_reverse_doc); - + MakeUnaryStringBatchKernelWithState<AsciiCenter>("ascii_center", registry, &ascii_center_doc); MakeUnaryStringBatchKernelWithState<AsciiLPad>("ascii_lpad", registry, &ascii_lpad_doc); @@ -4067,7 +4067,7 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { &utf8_center_doc); MakeUnaryStringBatchKernelWithState<Utf8LPad>("utf8_lpad", registry, &utf8_lpad_doc); MakeUnaryStringBatchKernelWithState<Utf8RPad>("utf8_rpad", registry, &utf8_rpad_doc); - + MakeUnaryStringBatchKernelWithState<AsciiTrim>("ascii_trim", registry, &ascii_trim_doc); MakeUnaryStringBatchKernelWithState<AsciiLTrim>("ascii_ltrim", registry, &ascii_ltrim_doc); @@ -4081,16 +4081,16 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { AddUnaryStringPredicate<IsAlphaAscii>("ascii_is_alpha", registry, &ascii_is_alpha_doc); AddUnaryStringPredicate<IsDecimalAscii>("ascii_is_decimal", registry, &ascii_is_decimal_doc); - // no is_digit for ascii, since it is the same as is_decimal + // no is_digit for ascii, since it is the same as is_decimal AddUnaryStringPredicate<IsLowerAscii>("ascii_is_lower", registry, &ascii_is_lower_doc); - // no is_numeric for ascii, since it is the same as is_decimal + // no is_numeric for ascii, since it is the same as is_decimal AddUnaryStringPredicate<IsPrintableAscii>("ascii_is_printable", registry, &ascii_is_printable_doc); AddUnaryStringPredicate<IsSpaceAscii>("ascii_is_space", registry, &ascii_is_space_doc); AddUnaryStringPredicate<IsTitleAscii>("ascii_is_title", registry, &ascii_is_title_doc); AddUnaryStringPredicate<IsUpperAscii>("ascii_is_upper", registry, &ascii_is_upper_doc); - -#ifdef ARROW_WITH_UTF8PROC + +#ifdef ARROW_WITH_UTF8PROC MakeUnaryStringUTF8TransformKernel<UTF8Upper>("utf8_upper", registry, &utf8_upper_doc); MakeUnaryStringUTF8TransformKernel<UTF8Lower>("utf8_lower", registry, &utf8_lower_doc); MakeUnaryStringBatchKernel<UTF8TrimWhitespace>("utf8_trim_whitespace", registry, @@ -4102,7 +4102,7 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { MakeUnaryStringBatchKernelWithState<UTF8Trim>("utf8_trim", registry, &utf8_trim_doc); MakeUnaryStringBatchKernelWithState<UTF8LTrim>("utf8_ltrim", registry, &utf8_ltrim_doc); MakeUnaryStringBatchKernelWithState<UTF8RTrim>("utf8_rtrim", registry, &utf8_rtrim_doc); - + AddUnaryStringPredicate<IsAlphaNumericUnicode>("utf8_is_alnum", registry, &utf8_is_alnum_doc); AddUnaryStringPredicate<IsAlphaUnicode>("utf8_is_alpha", registry, &utf8_is_alpha_doc); @@ -4117,11 +4117,11 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { AddUnaryStringPredicate<IsSpaceUnicode>("utf8_is_space", registry, &utf8_is_space_doc); AddUnaryStringPredicate<IsTitleUnicode>("utf8_is_title", registry, &utf8_is_title_doc); AddUnaryStringPredicate<IsUpperUnicode>("utf8_is_upper", registry, &utf8_is_upper_doc); -#endif - - AddBinaryLength(registry); +#endif + + AddBinaryLength(registry); AddUtf8Length(registry); - AddMatchSubstring(registry); + AddMatchSubstring(registry); AddFindSubstring(registry); AddCountSubstring(registry); MakeUnaryStringBatchKernelWithState<ReplaceSubStringPlain>( @@ -4136,10 +4136,10 @@ void RegisterScalarStringAscii(FunctionRegistry* registry) { AddReplaceSlice(registry); AddSlice(registry); AddSplit(registry); - AddStrptime(registry); + AddStrptime(registry); AddBinaryJoin(registry); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_validity.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_validity.cc index befb116348..ead88abc0f 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_validity.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/scalar_validity.cc @@ -1,65 +1,65 @@ -// 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. - +// 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. + #include <cmath> -#include "arrow/compute/kernels/common.h" - -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_ops.h" - -namespace arrow { - -using internal::CopyBitmap; -using internal::InvertBitmap; - -namespace compute { -namespace internal { -namespace { - -struct IsValidOperator { +#include "arrow/compute/kernels/common.h" + +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_ops.h" + +namespace arrow { + +using internal::CopyBitmap; +using internal::InvertBitmap; + +namespace compute { +namespace internal { +namespace { + +struct IsValidOperator { static Status Call(KernelContext* ctx, const Scalar& in, Scalar* out) { - checked_cast<BooleanScalar*>(out)->value = in.is_valid; + checked_cast<BooleanScalar*>(out)->value = in.is_valid; return Status::OK(); - } - + } + static Status Call(KernelContext* ctx, const ArrayData& arr, ArrayData* out) { - DCHECK_EQ(out->offset, 0); - DCHECK_LE(out->length, arr.length); - if (arr.MayHaveNulls()) { - // Input has nulls => output is the null (validity) bitmap. - // To avoid copying the null bitmap, slice from the starting byte offset - // and set the offset to the remaining bit offset. - out->offset = arr.offset % 8; - out->buffers[1] = - arr.offset == 0 ? arr.buffers[0] - : SliceBuffer(arr.buffers[0], arr.offset / 8, - BitUtil::BytesForBits(out->length + out->offset)); + DCHECK_EQ(out->offset, 0); + DCHECK_LE(out->length, arr.length); + if (arr.MayHaveNulls()) { + // Input has nulls => output is the null (validity) bitmap. + // To avoid copying the null bitmap, slice from the starting byte offset + // and set the offset to the remaining bit offset. + out->offset = arr.offset % 8; + out->buffers[1] = + arr.offset == 0 ? arr.buffers[0] + : SliceBuffer(arr.buffers[0], arr.offset / 8, + BitUtil::BytesForBits(out->length + out->offset)); return Status::OK(); - } - - // Input has no nulls => output is entirely true. + } + + // Input has no nulls => output is entirely true. ARROW_ASSIGN_OR_RAISE(out->buffers[1], ctx->AllocateBitmap(out->length + out->offset)); - BitUtil::SetBitsTo(out->buffers[1]->mutable_data(), out->offset, out->length, true); + BitUtil::SetBitsTo(out->buffers[1]->mutable_data(), out->offset, out->length, true); return Status::OK(); - } -}; - + } +}; + struct IsFiniteOperator { template <typename OutType, typename InType> static constexpr OutType Call(KernelContext*, const InType& value, Status*) { @@ -74,49 +74,49 @@ struct IsInfOperator { } }; -struct IsNullOperator { +struct IsNullOperator { static Status Call(KernelContext* ctx, const Scalar& in, Scalar* out) { - checked_cast<BooleanScalar*>(out)->value = !in.is_valid; + checked_cast<BooleanScalar*>(out)->value = !in.is_valid; return Status::OK(); - } - + } + static Status Call(KernelContext* ctx, const ArrayData& arr, ArrayData* out) { - if (arr.MayHaveNulls()) { - // Input has nulls => output is the inverted null (validity) bitmap. - InvertBitmap(arr.buffers[0]->data(), arr.offset, arr.length, - out->buffers[1]->mutable_data(), out->offset); + if (arr.MayHaveNulls()) { + // Input has nulls => output is the inverted null (validity) bitmap. + InvertBitmap(arr.buffers[0]->data(), arr.offset, arr.length, + out->buffers[1]->mutable_data(), out->offset); } else { // Input has no nulls => output is entirely false. BitUtil::SetBitsTo(out->buffers[1]->mutable_data(), out->offset, out->length, false); - } + } return Status::OK(); } }; - + struct IsNanOperator { template <typename OutType, typename InType> static constexpr OutType Call(KernelContext*, const InType& value, Status*) { return std::isnan(value); - } -}; - + } +}; + void MakeFunction(std::string name, const FunctionDoc* doc, std::vector<InputType> in_types, OutputType out_type, - ArrayKernelExec exec, FunctionRegistry* registry, - MemAllocation::type mem_allocation, bool can_write_into_slices) { - Arity arity{static_cast<int>(in_types.size())}; + ArrayKernelExec exec, FunctionRegistry* registry, + MemAllocation::type mem_allocation, bool can_write_into_slices) { + Arity arity{static_cast<int>(in_types.size())}; auto func = std::make_shared<ScalarFunction>(name, arity, doc); - - ScalarKernel kernel(std::move(in_types), out_type, exec); - kernel.null_handling = NullHandling::OUTPUT_NOT_NULL; - kernel.can_write_into_slices = can_write_into_slices; - kernel.mem_allocation = mem_allocation; - - DCHECK_OK(func->AddKernel(std::move(kernel))); - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + + ScalarKernel kernel(std::move(in_types), out_type, exec); + kernel.null_handling = NullHandling::OUTPUT_NOT_NULL; + kernel.can_write_into_slices = can_write_into_slices; + kernel.mem_allocation = mem_allocation; + + DCHECK_OK(func->AddKernel(std::move(kernel))); + DCHECK_OK(registry->AddFunction(std::move(func))); +} + template <typename InType, typename Op> void AddFloatValidityKernel(const std::shared_ptr<DataType>& ty, ScalarFunction* func) { DCHECK_OK(func->AddKernel({ty}, boolean(), @@ -154,40 +154,40 @@ std::shared_ptr<ScalarFunction> MakeIsNanFunction(std::string name, } Status IsValidExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const Datum& arg0 = batch[0]; - if (arg0.type()->id() == Type::NA) { - auto false_value = std::make_shared<BooleanScalar>(false); - if (arg0.kind() == Datum::SCALAR) { + const Datum& arg0 = batch[0]; + if (arg0.type()->id() == Type::NA) { + auto false_value = std::make_shared<BooleanScalar>(false); + if (arg0.kind() == Datum::SCALAR) { out->value = false_value; - } else { - std::shared_ptr<Array> false_values; + } else { + std::shared_ptr<Array> false_values; RETURN_NOT_OK(MakeArrayFromScalar(*false_value, out->length(), ctx->memory_pool()) .Value(&false_values)); - out->value = false_values->data(); - } + out->value = false_values->data(); + } return Status::OK(); - } else { + } else { return applicator::SimpleUnary<IsValidOperator>(ctx, batch, out); - } -} - + } +} + Status IsNullExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - const Datum& arg0 = batch[0]; - if (arg0.type()->id() == Type::NA) { - if (arg0.kind() == Datum::SCALAR) { + const Datum& arg0 = batch[0]; + if (arg0.type()->id() == Type::NA) { + if (arg0.kind() == Datum::SCALAR) { out->value = std::make_shared<BooleanScalar>(true); - } else { - // Data is preallocated - ArrayData* out_arr = out->mutable_array(); - BitUtil::SetBitsTo(out_arr->buffers[1]->mutable_data(), out_arr->offset, - out_arr->length, true); - } + } else { + // Data is preallocated + ArrayData* out_arr = out->mutable_array(); + BitUtil::SetBitsTo(out_arr->buffers[1]->mutable_data(), out_arr->offset, + out_arr->length, true); + } return Status::OK(); - } else { + } else { return applicator::SimpleUnary<IsNullOperator>(ctx, batch, out); - } -} - + } +} + const FunctionDoc is_valid_doc( "Return true if non-null", ("For each input value, emit true iff the value is valid (non-null)."), {"values"}); @@ -210,21 +210,21 @@ const FunctionDoc is_nan_doc("Return true if NaN", ("For each input value, emit true iff the value is NaN."), {"values"}); -} // namespace - -void RegisterScalarValidity(FunctionRegistry* registry) { +} // namespace + +void RegisterScalarValidity(FunctionRegistry* registry) { MakeFunction("is_valid", &is_valid_doc, {ValueDescr::ANY}, boolean(), IsValidExec, registry, MemAllocation::NO_PREALLOCATE, /*can_write_into_slices=*/false); - + MakeFunction("is_null", &is_null_doc, {ValueDescr::ANY}, boolean(), IsNullExec, registry, MemAllocation::PREALLOCATE, - /*can_write_into_slices=*/true); + /*can_write_into_slices=*/true); DCHECK_OK(registry->AddFunction(MakeIsFiniteFunction("is_finite", &is_finite_doc))); DCHECK_OK(registry->AddFunction(MakeIsInfFunction("is_inf", &is_inf_doc))); DCHECK_OK(registry->AddFunction(MakeIsNanFunction("is_nan", &is_nan_doc))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.cc index df011f802c..846fa26baf 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.cc @@ -1,62 +1,62 @@ -// 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. - -#include "arrow/compute/kernels/util_internal.h" - -#include <cstdint> - -#include "arrow/array/data.h" -#include "arrow/type.h" -#include "arrow/util/checked_cast.h" - -namespace arrow { - -using internal::checked_cast; - -namespace compute { -namespace internal { - -const uint8_t* GetValidityBitmap(const ArrayData& data) { - const uint8_t* bitmap = nullptr; - if (data.buffers[0]) { - bitmap = data.buffers[0]->data(); - } - return bitmap; -} - -int GetBitWidth(const DataType& type) { - return checked_cast<const FixedWidthType&>(type).bit_width(); -} - -PrimitiveArg GetPrimitiveArg(const ArrayData& arr) { - PrimitiveArg arg; - arg.is_valid = GetValidityBitmap(arr); - arg.data = arr.buffers[1]->data(); - arg.bit_width = GetBitWidth(*arr.type); - arg.offset = arr.offset; - arg.length = arr.length; - if (arg.bit_width > 1) { - arg.data += arr.offset * arg.bit_width / 8; - } - // This may be kUnknownNullCount +// 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. + +#include "arrow/compute/kernels/util_internal.h" + +#include <cstdint> + +#include "arrow/array/data.h" +#include "arrow/type.h" +#include "arrow/util/checked_cast.h" + +namespace arrow { + +using internal::checked_cast; + +namespace compute { +namespace internal { + +const uint8_t* GetValidityBitmap(const ArrayData& data) { + const uint8_t* bitmap = nullptr; + if (data.buffers[0]) { + bitmap = data.buffers[0]->data(); + } + return bitmap; +} + +int GetBitWidth(const DataType& type) { + return checked_cast<const FixedWidthType&>(type).bit_width(); +} + +PrimitiveArg GetPrimitiveArg(const ArrayData& arr) { + PrimitiveArg arg; + arg.is_valid = GetValidityBitmap(arr); + arg.data = arr.buffers[1]->data(); + arg.bit_width = GetBitWidth(*arr.type); + arg.offset = arr.offset; + arg.length = arr.length; + if (arg.bit_width > 1) { + arg.data += arr.offset * arg.bit_width / 8; + } + // This may be kUnknownNullCount arg.null_count = (arg.is_valid != nullptr) ? arr.null_count.load() : 0; - return arg; -} - + return arg; +} + ArrayKernelExec TrivialScalarUnaryAsArraysExec(ArrayKernelExec exec, NullHandling::type null_handling) { return [=](KernelContext* ctx, const ExecBatch& batch, Datum* out) -> Status { @@ -77,6 +77,6 @@ ArrayKernelExec TrivialScalarUnaryAsArraysExec(ArrayKernelExec exec, }; } -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.h index 03d7c0da2b..394e08da58 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/util_internal.h @@ -1,35 +1,35 @@ -// 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 <cstdint> +// 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 <cstdint> #include <utility> - + #include "arrow/array/util.h" -#include "arrow/buffer.h" +#include "arrow/buffer.h" #include "arrow/compute/kernels/codegen_internal.h" #include "arrow/compute/type_fwd.h" #include "arrow/util/bit_run_reader.h" - -namespace arrow { -namespace compute { -namespace internal { - + +namespace arrow { +namespace compute { +namespace internal { + // Used in some kernels and testing - not provided by default in MSVC // and _USE_MATH_DEFINES is not reliable with unity builds #ifndef M_PI @@ -42,31 +42,31 @@ namespace internal { #define M_PI_4 0.785398163397448309616 #endif -// An internal data structure for unpacking a primitive argument to pass to a -// kernel implementation -struct PrimitiveArg { - const uint8_t* is_valid; - // If the bit_width is a multiple of 8 (i.e. not boolean), then "data" should - // be shifted by offset * (bit_width / 8). For bit-packed data, the offset - // must be used when indexing. - const uint8_t* data; - int bit_width; - int64_t length; - int64_t offset; - // This may be kUnknownNullCount if the null_count has not yet been computed, - // so use null_count != 0 to determine "may have nulls". - int64_t null_count; -}; - -// Get validity bitmap data or return nullptr if there is no validity buffer -const uint8_t* GetValidityBitmap(const ArrayData& data); - -int GetBitWidth(const DataType& type); - -// Reduce code size by dealing with the unboxing of the kernel inputs once -// rather than duplicating compiled code to do all these in each kernel. -PrimitiveArg GetPrimitiveArg(const ArrayData& arr); - +// An internal data structure for unpacking a primitive argument to pass to a +// kernel implementation +struct PrimitiveArg { + const uint8_t* is_valid; + // If the bit_width is a multiple of 8 (i.e. not boolean), then "data" should + // be shifted by offset * (bit_width / 8). For bit-packed data, the offset + // must be used when indexing. + const uint8_t* data; + int bit_width; + int64_t length; + int64_t offset; + // This may be kUnknownNullCount if the null_count has not yet been computed, + // so use null_count != 0 to determine "may have nulls". + int64_t null_count; +}; + +// Get validity bitmap data or return nullptr if there is no validity buffer +const uint8_t* GetValidityBitmap(const ArrayData& data); + +int GetBitWidth(const DataType& type); + +// Reduce code size by dealing with the unboxing of the kernel inputs once +// rather than duplicating compiled code to do all these in each kernel. +PrimitiveArg GetPrimitiveArg(const ArrayData& arr); + // Augment a unary ArrayKernelExec which supports only array-like inputs with support for // scalar inputs. Scalars will be transformed to 1-long arrays with the scalar's value (or // null if the scalar is null) as its only element. This 1-long array will be passed to @@ -161,6 +161,6 @@ int64_t CopyNonNullValues(const Datum& datum, T* out) { return n; } -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_hash.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_hash.cc index 9c37f23faf..a68e78130f 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_hash.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_hash.cc @@ -1,173 +1,173 @@ -// 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. - -#include <cstring> -#include <mutex> - -#include "arrow/array/array_base.h" -#include "arrow/array/array_dict.h" -#include "arrow/array/array_nested.h" -#include "arrow/array/builder_primitive.h" +// 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. + +#include <cstring> +#include <mutex> + +#include "arrow/array/array_base.h" +#include "arrow/array/array_dict.h" +#include "arrow/array/array_nested.h" +#include "arrow/array/builder_primitive.h" #include "arrow/array/concatenate.h" -#include "arrow/array/dict_internal.h" -#include "arrow/array/util.h" -#include "arrow/compute/api_vector.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/result.h" -#include "arrow/util/hashing.h" -#include "arrow/util/make_unique.h" - -namespace arrow { - -using internal::DictionaryTraits; -using internal::HashTraits; - -namespace compute { -namespace internal { - -namespace { - -class ActionBase { - public: - ActionBase(const std::shared_ptr<DataType>& type, MemoryPool* pool) - : type_(type), pool_(pool) {} - - protected: - std::shared_ptr<DataType> type_; - MemoryPool* pool_; -}; - -// ---------------------------------------------------------------------- -// Unique - -class UniqueAction final : public ActionBase { - public: - using ActionBase::ActionBase; - - static constexpr bool with_error_status = false; - +#include "arrow/array/dict_internal.h" +#include "arrow/array/util.h" +#include "arrow/compute/api_vector.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/result.h" +#include "arrow/util/hashing.h" +#include "arrow/util/make_unique.h" + +namespace arrow { + +using internal::DictionaryTraits; +using internal::HashTraits; + +namespace compute { +namespace internal { + +namespace { + +class ActionBase { + public: + ActionBase(const std::shared_ptr<DataType>& type, MemoryPool* pool) + : type_(type), pool_(pool) {} + + protected: + std::shared_ptr<DataType> type_; + MemoryPool* pool_; +}; + +// ---------------------------------------------------------------------- +// Unique + +class UniqueAction final : public ActionBase { + public: + using ActionBase::ActionBase; + + static constexpr bool with_error_status = false; + UniqueAction(const std::shared_ptr<DataType>& type, const FunctionOptions* options, MemoryPool* pool) : ActionBase(type, pool) {} - Status Reset() { return Status::OK(); } - - Status Reserve(const int64_t length) { return Status::OK(); } - - template <class Index> - void ObserveNullFound(Index index) {} - - template <class Index> - void ObserveNullNotFound(Index index) {} - - template <class Index> - void ObserveFound(Index index) {} - - template <class Index> - void ObserveNotFound(Index index) {} - + Status Reset() { return Status::OK(); } + + Status Reserve(const int64_t length) { return Status::OK(); } + + template <class Index> + void ObserveNullFound(Index index) {} + + template <class Index> + void ObserveNullNotFound(Index index) {} + + template <class Index> + void ObserveFound(Index index) {} + + template <class Index> + void ObserveNotFound(Index index) {} + bool ShouldEncodeNulls() { return true; } - Status Flush(Datum* out) { return Status::OK(); } - - Status FlushFinal(Datum* out) { return Status::OK(); } -}; - -// ---------------------------------------------------------------------- -// Count values - -class ValueCountsAction final : ActionBase { - public: - using ActionBase::ActionBase; - - static constexpr bool with_error_status = true; - + Status Flush(Datum* out) { return Status::OK(); } + + Status FlushFinal(Datum* out) { return Status::OK(); } +}; + +// ---------------------------------------------------------------------- +// Count values + +class ValueCountsAction final : ActionBase { + public: + using ActionBase::ActionBase; + + static constexpr bool with_error_status = true; + ValueCountsAction(const std::shared_ptr<DataType>& type, const FunctionOptions* options, MemoryPool* pool) - : ActionBase(type, pool), count_builder_(pool) {} - - Status Reserve(const int64_t length) { - // builder size is independent of input array size. - return Status::OK(); - } - - Status Reset() { - count_builder_.Reset(); - return Status::OK(); - } - - // Don't do anything on flush because we don't want to finalize the builder - // or incur the cost of memory copies. - Status Flush(Datum* out) { return Status::OK(); } - - // Return the counts corresponding the MemoTable keys. - Status FlushFinal(Datum* out) { - std::shared_ptr<ArrayData> result; - RETURN_NOT_OK(count_builder_.FinishInternal(&result)); - out->value = std::move(result); - return Status::OK(); - } - - template <class Index> - void ObserveNullFound(Index index) { - count_builder_[index]++; - } - - template <class Index> - void ObserveNullNotFound(Index index) { - ARROW_LOG(FATAL) << "ObserveNullNotFound without err_status should not be called"; - } - - template <class Index> - void ObserveNullNotFound(Index index, Status* status) { - Status s = count_builder_.Append(1); - if (ARROW_PREDICT_FALSE(!s.ok())) { - *status = s; - } - } - - template <class Index> - void ObserveFound(Index slot) { - count_builder_[slot]++; - } - - template <class Index> - void ObserveNotFound(Index slot, Status* status) { - Status s = count_builder_.Append(1); - if (ARROW_PREDICT_FALSE(!s.ok())) { - *status = s; - } - } - + : ActionBase(type, pool), count_builder_(pool) {} + + Status Reserve(const int64_t length) { + // builder size is independent of input array size. + return Status::OK(); + } + + Status Reset() { + count_builder_.Reset(); + return Status::OK(); + } + + // Don't do anything on flush because we don't want to finalize the builder + // or incur the cost of memory copies. + Status Flush(Datum* out) { return Status::OK(); } + + // Return the counts corresponding the MemoTable keys. + Status FlushFinal(Datum* out) { + std::shared_ptr<ArrayData> result; + RETURN_NOT_OK(count_builder_.FinishInternal(&result)); + out->value = std::move(result); + return Status::OK(); + } + + template <class Index> + void ObserveNullFound(Index index) { + count_builder_[index]++; + } + + template <class Index> + void ObserveNullNotFound(Index index) { + ARROW_LOG(FATAL) << "ObserveNullNotFound without err_status should not be called"; + } + + template <class Index> + void ObserveNullNotFound(Index index, Status* status) { + Status s = count_builder_.Append(1); + if (ARROW_PREDICT_FALSE(!s.ok())) { + *status = s; + } + } + + template <class Index> + void ObserveFound(Index slot) { + count_builder_[slot]++; + } + + template <class Index> + void ObserveNotFound(Index slot, Status* status) { + Status s = count_builder_.Append(1); + if (ARROW_PREDICT_FALSE(!s.ok())) { + *status = s; + } + } + bool ShouldEncodeNulls() const { return true; } - private: - Int64Builder count_builder_; -}; - -// ---------------------------------------------------------------------- -// Dictionary encode implementation - -class DictEncodeAction final : public ActionBase { - public: - using ActionBase::ActionBase; - - static constexpr bool with_error_status = false; - + private: + Int64Builder count_builder_; +}; + +// ---------------------------------------------------------------------- +// Dictionary encode implementation + +class DictEncodeAction final : public ActionBase { + public: + using ActionBase::ActionBase; + + static constexpr bool with_error_status = false; + DictEncodeAction(const std::shared_ptr<DataType>& type, const FunctionOptions* options, MemoryPool* pool) : ActionBase(type, pool), indices_builder_(pool) { @@ -175,174 +175,174 @@ class DictEncodeAction final : public ActionBase { encode_options_ = *options_ptr; } } - - Status Reset() { - indices_builder_.Reset(); - return Status::OK(); - } - - Status Reserve(const int64_t length) { return indices_builder_.Reserve(length); } - - template <class Index> - void ObserveNullFound(Index index) { + + Status Reset() { + indices_builder_.Reset(); + return Status::OK(); + } + + Status Reserve(const int64_t length) { return indices_builder_.Reserve(length); } + + template <class Index> + void ObserveNullFound(Index index) { if (encode_options_.null_encoding_behavior == DictionaryEncodeOptions::MASK) { indices_builder_.UnsafeAppendNull(); } else { indices_builder_.UnsafeAppend(index); } - } - - template <class Index> - void ObserveNullNotFound(Index index) { + } + + template <class Index> + void ObserveNullNotFound(Index index) { ObserveNullFound(index); - } - - template <class Index> - void ObserveFound(Index index) { - indices_builder_.UnsafeAppend(index); - } - - template <class Index> - void ObserveNotFound(Index index) { - ObserveFound(index); - } - + } + + template <class Index> + void ObserveFound(Index index) { + indices_builder_.UnsafeAppend(index); + } + + template <class Index> + void ObserveNotFound(Index index) { + ObserveFound(index); + } + bool ShouldEncodeNulls() { return encode_options_.null_encoding_behavior == DictionaryEncodeOptions::ENCODE; } - Status Flush(Datum* out) { - std::shared_ptr<ArrayData> result; - RETURN_NOT_OK(indices_builder_.FinishInternal(&result)); - out->value = std::move(result); - return Status::OK(); - } - - Status FlushFinal(Datum* out) { return Status::OK(); } - - private: - Int32Builder indices_builder_; + Status Flush(Datum* out) { + std::shared_ptr<ArrayData> result; + RETURN_NOT_OK(indices_builder_.FinishInternal(&result)); + out->value = std::move(result); + return Status::OK(); + } + + Status FlushFinal(Datum* out) { return Status::OK(); } + + private: + Int32Builder indices_builder_; DictionaryEncodeOptions encode_options_; -}; - -class HashKernel : public KernelState { - public: +}; + +class HashKernel : public KernelState { + public: HashKernel() : options_(nullptr) {} explicit HashKernel(const FunctionOptions* options) : options_(options) {} - // Reset for another run. - virtual Status Reset() = 0; - - // Flush out accumulated results from the last invocation of Call. - virtual Status Flush(Datum* out) = 0; - // Flush out accumulated results across all invocations of Call. The kernel - // should not be used until after Reset() is called. - virtual Status FlushFinal(Datum* out) = 0; - // Get the values (keys) accumulated in the dictionary so far. - virtual Status GetDictionary(std::shared_ptr<ArrayData>* out) = 0; - - virtual std::shared_ptr<DataType> value_type() const = 0; - - Status Append(KernelContext* ctx, const ArrayData& input) { - std::lock_guard<std::mutex> guard(lock_); - return Append(input); - } - - // Prepare the Action for the given input (e.g. reserve appropriately sized - // data structures) and visit the given input with Action. - virtual Status Append(const ArrayData& arr) = 0; - - protected: + // Reset for another run. + virtual Status Reset() = 0; + + // Flush out accumulated results from the last invocation of Call. + virtual Status Flush(Datum* out) = 0; + // Flush out accumulated results across all invocations of Call. The kernel + // should not be used until after Reset() is called. + virtual Status FlushFinal(Datum* out) = 0; + // Get the values (keys) accumulated in the dictionary so far. + virtual Status GetDictionary(std::shared_ptr<ArrayData>* out) = 0; + + virtual std::shared_ptr<DataType> value_type() const = 0; + + Status Append(KernelContext* ctx, const ArrayData& input) { + std::lock_guard<std::mutex> guard(lock_); + return Append(input); + } + + // Prepare the Action for the given input (e.g. reserve appropriately sized + // data structures) and visit the given input with Action. + virtual Status Append(const ArrayData& arr) = 0; + + protected: const FunctionOptions* options_; - std::mutex lock_; -}; - -// ---------------------------------------------------------------------- -// Base class for all "regular" hash kernel implementations -// (NullType has a separate implementation) - -template <typename Type, typename Scalar, typename Action, + std::mutex lock_; +}; + +// ---------------------------------------------------------------------- +// Base class for all "regular" hash kernel implementations +// (NullType has a separate implementation) + +template <typename Type, typename Scalar, typename Action, bool with_error_status = Action::with_error_status> -class RegularHashKernel : public HashKernel { - public: +class RegularHashKernel : public HashKernel { + public: RegularHashKernel(const std::shared_ptr<DataType>& type, const FunctionOptions* options, MemoryPool* pool) : HashKernel(options), pool_(pool), type_(type), action_(type, options, pool) {} - - Status Reset() override { - memo_table_.reset(new MemoTable(pool_, 0)); - return action_.Reset(); - } - - Status Append(const ArrayData& arr) override { - RETURN_NOT_OK(action_.Reserve(arr.length)); - return DoAppend(arr); - } - - Status Flush(Datum* out) override { return action_.Flush(out); } - - Status FlushFinal(Datum* out) override { return action_.FlushFinal(out); } - - Status GetDictionary(std::shared_ptr<ArrayData>* out) override { - return DictionaryTraits<Type>::GetDictionaryArrayData(pool_, type_, *memo_table_, - 0 /* start_offset */, out); - } - - std::shared_ptr<DataType> value_type() const override { return type_; } - - template <bool HasError = with_error_status> - enable_if_t<!HasError, Status> DoAppend(const ArrayData& arr) { - return VisitArrayDataInline<Type>( - arr, - [this](Scalar v) { - auto on_found = [this](int32_t memo_index) { - action_.ObserveFound(memo_index); - }; - auto on_not_found = [this](int32_t memo_index) { - action_.ObserveNotFound(memo_index); - }; - - int32_t unused_memo_index; - return memo_table_->GetOrInsert(v, std::move(on_found), std::move(on_not_found), - &unused_memo_index); - }, - [this]() { + + Status Reset() override { + memo_table_.reset(new MemoTable(pool_, 0)); + return action_.Reset(); + } + + Status Append(const ArrayData& arr) override { + RETURN_NOT_OK(action_.Reserve(arr.length)); + return DoAppend(arr); + } + + Status Flush(Datum* out) override { return action_.Flush(out); } + + Status FlushFinal(Datum* out) override { return action_.FlushFinal(out); } + + Status GetDictionary(std::shared_ptr<ArrayData>* out) override { + return DictionaryTraits<Type>::GetDictionaryArrayData(pool_, type_, *memo_table_, + 0 /* start_offset */, out); + } + + std::shared_ptr<DataType> value_type() const override { return type_; } + + template <bool HasError = with_error_status> + enable_if_t<!HasError, Status> DoAppend(const ArrayData& arr) { + return VisitArrayDataInline<Type>( + arr, + [this](Scalar v) { + auto on_found = [this](int32_t memo_index) { + action_.ObserveFound(memo_index); + }; + auto on_not_found = [this](int32_t memo_index) { + action_.ObserveNotFound(memo_index); + }; + + int32_t unused_memo_index; + return memo_table_->GetOrInsert(v, std::move(on_found), std::move(on_not_found), + &unused_memo_index); + }, + [this]() { if (action_.ShouldEncodeNulls()) { - auto on_found = [this](int32_t memo_index) { - action_.ObserveNullFound(memo_index); - }; - auto on_not_found = [this](int32_t memo_index) { - action_.ObserveNullNotFound(memo_index); - }; - memo_table_->GetOrInsertNull(std::move(on_found), std::move(on_not_found)); - } else { - action_.ObserveNullNotFound(-1); - } - return Status::OK(); - }); - } - - template <bool HasError = with_error_status> - enable_if_t<HasError, Status> DoAppend(const ArrayData& arr) { - return VisitArrayDataInline<Type>( - arr, - [this](Scalar v) { - Status s = Status::OK(); - auto on_found = [this](int32_t memo_index) { - action_.ObserveFound(memo_index); - }; - auto on_not_found = [this, &s](int32_t memo_index) { - action_.ObserveNotFound(memo_index, &s); - }; - - int32_t unused_memo_index; - RETURN_NOT_OK(memo_table_->GetOrInsert( - v, std::move(on_found), std::move(on_not_found), &unused_memo_index)); - return s; - }, - [this]() { - // Null - Status s = Status::OK(); + auto on_found = [this](int32_t memo_index) { + action_.ObserveNullFound(memo_index); + }; + auto on_not_found = [this](int32_t memo_index) { + action_.ObserveNullNotFound(memo_index); + }; + memo_table_->GetOrInsertNull(std::move(on_found), std::move(on_not_found)); + } else { + action_.ObserveNullNotFound(-1); + } + return Status::OK(); + }); + } + + template <bool HasError = with_error_status> + enable_if_t<HasError, Status> DoAppend(const ArrayData& arr) { + return VisitArrayDataInline<Type>( + arr, + [this](Scalar v) { + Status s = Status::OK(); + auto on_found = [this](int32_t memo_index) { + action_.ObserveFound(memo_index); + }; + auto on_not_found = [this, &s](int32_t memo_index) { + action_.ObserveNotFound(memo_index, &s); + }; + + int32_t unused_memo_index; + RETURN_NOT_OK(memo_table_->GetOrInsert( + v, std::move(on_found), std::move(on_not_found), &unused_memo_index)); + return s; + }, + [this]() { + // Null + Status s = Status::OK(); auto on_found = [this](int32_t memo_index) { action_.ObserveNullFound(memo_index); }; @@ -350,49 +350,49 @@ class RegularHashKernel : public HashKernel { action_.ObserveNullNotFound(memo_index, &s); }; if (action_.ShouldEncodeNulls()) { - memo_table_->GetOrInsertNull(std::move(on_found), std::move(on_not_found)); - } - return s; - }); - } - - protected: - using MemoTable = typename HashTraits<Type>::MemoTableType; - - MemoryPool* pool_; - std::shared_ptr<DataType> type_; - Action action_; - std::unique_ptr<MemoTable> memo_table_; -}; - -// ---------------------------------------------------------------------- -// Hash kernel implementation for nulls - + memo_table_->GetOrInsertNull(std::move(on_found), std::move(on_not_found)); + } + return s; + }); + } + + protected: + using MemoTable = typename HashTraits<Type>::MemoTableType; + + MemoryPool* pool_; + std::shared_ptr<DataType> type_; + Action action_; + std::unique_ptr<MemoTable> memo_table_; +}; + +// ---------------------------------------------------------------------- +// Hash kernel implementation for nulls + template <typename Action, bool with_error_status = Action::with_error_status> -class NullHashKernel : public HashKernel { - public: +class NullHashKernel : public HashKernel { + public: NullHashKernel(const std::shared_ptr<DataType>& type, const FunctionOptions* options, MemoryPool* pool) : pool_(pool), type_(type), action_(type, options, pool) {} - - Status Reset() override { return action_.Reset(); } - + + Status Reset() override { return action_.Reset(); } + Status Append(const ArrayData& arr) override { return DoAppend(arr); } template <bool HasError = with_error_status> enable_if_t<!HasError, Status> DoAppend(const ArrayData& arr) { - RETURN_NOT_OK(action_.Reserve(arr.length)); - for (int64_t i = 0; i < arr.length; ++i) { - if (i == 0) { + RETURN_NOT_OK(action_.Reserve(arr.length)); + for (int64_t i = 0; i < arr.length; ++i) { + if (i == 0) { seen_null_ = true; - action_.ObserveNullNotFound(0); - } else { - action_.ObserveNullFound(0); - } - } - return Status::OK(); - } - + action_.ObserveNullNotFound(0); + } else { + action_.ObserveNullFound(0); + } + } + return Status::OK(); + } + template <bool HasError = with_error_status> enable_if_t<HasError, Status> DoAppend(const ArrayData& arr) { Status s = Status::OK(); @@ -408,41 +408,41 @@ class NullHashKernel : public HashKernel { return s; } - Status Flush(Datum* out) override { return action_.Flush(out); } - Status FlushFinal(Datum* out) override { return action_.FlushFinal(out); } - - Status GetDictionary(std::shared_ptr<ArrayData>* out) override { + Status Flush(Datum* out) override { return action_.Flush(out); } + Status FlushFinal(Datum* out) override { return action_.FlushFinal(out); } + + Status GetDictionary(std::shared_ptr<ArrayData>* out) override { std::shared_ptr<NullArray> null_array; if (seen_null_) { null_array = std::make_shared<NullArray>(1); } else { null_array = std::make_shared<NullArray>(0); } - *out = null_array->data(); - return Status::OK(); - } - - std::shared_ptr<DataType> value_type() const override { return type_; } - - protected: - MemoryPool* pool_; - std::shared_ptr<DataType> type_; + *out = null_array->data(); + return Status::OK(); + } + + std::shared_ptr<DataType> value_type() const override { return type_; } + + protected: + MemoryPool* pool_; + std::shared_ptr<DataType> type_; bool seen_null_ = false; - Action action_; -}; - -// ---------------------------------------------------------------------- -// Hashing for dictionary type - -class DictionaryHashKernel : public HashKernel { - public: - explicit DictionaryHashKernel(std::unique_ptr<HashKernel> indices_kernel) - : indices_kernel_(std::move(indices_kernel)) {} - - Status Reset() override { return indices_kernel_->Reset(); } - + Action action_; +}; + +// ---------------------------------------------------------------------- +// Hashing for dictionary type + +class DictionaryHashKernel : public HashKernel { + public: + explicit DictionaryHashKernel(std::unique_ptr<HashKernel> indices_kernel) + : indices_kernel_(std::move(indices_kernel)) {} + + Status Reset() override { return indices_kernel_->Reset(); } + Status Append(const ArrayData& arr) override { - if (!dictionary_) { + if (!dictionary_) { dictionary_ = arr.dictionary; } else if (!MakeArray(dictionary_)->Equals(*MakeArray(arr.dictionary))) { // NOTE: This approach computes a new dictionary unification per chunk. @@ -468,238 +468,238 @@ class DictionaryHashKernel : public HashKernel { auto tmp, arrow::internal::checked_cast<const DictionaryArray&>(*in_dict_array) .Transpose(arr.type, out_dict, transpose)); return indices_kernel_->Append(*tmp->data()); - } - - return indices_kernel_->Append(arr); - } - - Status Flush(Datum* out) override { return indices_kernel_->Flush(out); } - - Status FlushFinal(Datum* out) override { return indices_kernel_->FlushFinal(out); } - - Status GetDictionary(std::shared_ptr<ArrayData>* out) override { - return indices_kernel_->GetDictionary(out); - } - - std::shared_ptr<DataType> value_type() const override { - return indices_kernel_->value_type(); - } - - std::shared_ptr<ArrayData> dictionary() const { return dictionary_; } - - private: - std::unique_ptr<HashKernel> indices_kernel_; - std::shared_ptr<ArrayData> dictionary_; -}; - -// ---------------------------------------------------------------------- - -template <typename Type, typename Action, typename Enable = void> -struct HashKernelTraits {}; - -template <typename Type, typename Action> -struct HashKernelTraits<Type, Action, enable_if_null<Type>> { - using HashKernel = NullHashKernel<Action>; -}; - -template <typename Type, typename Action> -struct HashKernelTraits<Type, Action, enable_if_has_c_type<Type>> { - using HashKernel = RegularHashKernel<Type, typename Type::c_type, Action>; -}; - -template <typename Type, typename Action> -struct HashKernelTraits<Type, Action, enable_if_has_string_view<Type>> { - using HashKernel = RegularHashKernel<Type, util::string_view, Action>; -}; - -template <typename Type, typename Action> + } + + return indices_kernel_->Append(arr); + } + + Status Flush(Datum* out) override { return indices_kernel_->Flush(out); } + + Status FlushFinal(Datum* out) override { return indices_kernel_->FlushFinal(out); } + + Status GetDictionary(std::shared_ptr<ArrayData>* out) override { + return indices_kernel_->GetDictionary(out); + } + + std::shared_ptr<DataType> value_type() const override { + return indices_kernel_->value_type(); + } + + std::shared_ptr<ArrayData> dictionary() const { return dictionary_; } + + private: + std::unique_ptr<HashKernel> indices_kernel_; + std::shared_ptr<ArrayData> dictionary_; +}; + +// ---------------------------------------------------------------------- + +template <typename Type, typename Action, typename Enable = void> +struct HashKernelTraits {}; + +template <typename Type, typename Action> +struct HashKernelTraits<Type, Action, enable_if_null<Type>> { + using HashKernel = NullHashKernel<Action>; +}; + +template <typename Type, typename Action> +struct HashKernelTraits<Type, Action, enable_if_has_c_type<Type>> { + using HashKernel = RegularHashKernel<Type, typename Type::c_type, Action>; +}; + +template <typename Type, typename Action> +struct HashKernelTraits<Type, Action, enable_if_has_string_view<Type>> { + using HashKernel = RegularHashKernel<Type, util::string_view, Action>; +}; + +template <typename Type, typename Action> Result<std::unique_ptr<HashKernel>> HashInitImpl(KernelContext* ctx, const KernelInitArgs& args) { - using HashKernelType = typename HashKernelTraits<Type, Action>::HashKernel; + using HashKernelType = typename HashKernelTraits<Type, Action>::HashKernel; auto result = ::arrow::internal::make_unique<HashKernelType>( args.inputs[0].type, args.options, ctx->memory_pool()); RETURN_NOT_OK(result->Reset()); - return std::move(result); -} - -template <typename Type, typename Action> + return std::move(result); +} + +template <typename Type, typename Action> Result<std::unique_ptr<KernelState>> HashInit(KernelContext* ctx, const KernelInitArgs& args) { return HashInitImpl<Type, Action>(ctx, args); -} - -template <typename Action> -KernelInit GetHashInit(Type::type type_id) { - // ARROW-8933: Generate only a single hash kernel per physical data - // representation - switch (type_id) { - case Type::NA: - return HashInit<NullType, Action>; - case Type::BOOL: - return HashInit<BooleanType, Action>; - case Type::INT8: - case Type::UINT8: - return HashInit<UInt8Type, Action>; - case Type::INT16: - case Type::UINT16: - return HashInit<UInt16Type, Action>; - case Type::INT32: - case Type::UINT32: - case Type::FLOAT: - case Type::DATE32: - case Type::TIME32: - return HashInit<UInt32Type, Action>; - case Type::INT64: - case Type::UINT64: - case Type::DOUBLE: - case Type::DATE64: - case Type::TIME64: - case Type::TIMESTAMP: - case Type::DURATION: - return HashInit<UInt64Type, Action>; - case Type::BINARY: - case Type::STRING: - return HashInit<BinaryType, Action>; - case Type::LARGE_BINARY: - case Type::LARGE_STRING: - return HashInit<LargeBinaryType, Action>; - case Type::FIXED_SIZE_BINARY: +} + +template <typename Action> +KernelInit GetHashInit(Type::type type_id) { + // ARROW-8933: Generate only a single hash kernel per physical data + // representation + switch (type_id) { + case Type::NA: + return HashInit<NullType, Action>; + case Type::BOOL: + return HashInit<BooleanType, Action>; + case Type::INT8: + case Type::UINT8: + return HashInit<UInt8Type, Action>; + case Type::INT16: + case Type::UINT16: + return HashInit<UInt16Type, Action>; + case Type::INT32: + case Type::UINT32: + case Type::FLOAT: + case Type::DATE32: + case Type::TIME32: + return HashInit<UInt32Type, Action>; + case Type::INT64: + case Type::UINT64: + case Type::DOUBLE: + case Type::DATE64: + case Type::TIME64: + case Type::TIMESTAMP: + case Type::DURATION: + return HashInit<UInt64Type, Action>; + case Type::BINARY: + case Type::STRING: + return HashInit<BinaryType, Action>; + case Type::LARGE_BINARY: + case Type::LARGE_STRING: + return HashInit<LargeBinaryType, Action>; + case Type::FIXED_SIZE_BINARY: case Type::DECIMAL128: case Type::DECIMAL256: - return HashInit<FixedSizeBinaryType, Action>; - default: - DCHECK(false); - return nullptr; - } -} - + return HashInit<FixedSizeBinaryType, Action>; + default: + DCHECK(false); + return nullptr; + } +} + using DictionaryEncodeState = OptionsWrapper<DictionaryEncodeOptions>; -template <typename Action> +template <typename Action> Result<std::unique_ptr<KernelState>> DictionaryHashInit(KernelContext* ctx, const KernelInitArgs& args) { - const auto& dict_type = checked_cast<const DictionaryType&>(*args.inputs[0].type); + const auto& dict_type = checked_cast<const DictionaryType&>(*args.inputs[0].type); Result<std::unique_ptr<HashKernel>> indices_hasher; - switch (dict_type.index_type()->id()) { - case Type::INT8: - indices_hasher = HashInitImpl<UInt8Type, Action>(ctx, args); - break; - case Type::INT16: - indices_hasher = HashInitImpl<UInt16Type, Action>(ctx, args); - break; - case Type::INT32: - indices_hasher = HashInitImpl<UInt32Type, Action>(ctx, args); - break; - case Type::INT64: - indices_hasher = HashInitImpl<UInt64Type, Action>(ctx, args); - break; - default: - DCHECK(false) << "Unsupported dictionary index type"; - break; - } + switch (dict_type.index_type()->id()) { + case Type::INT8: + indices_hasher = HashInitImpl<UInt8Type, Action>(ctx, args); + break; + case Type::INT16: + indices_hasher = HashInitImpl<UInt16Type, Action>(ctx, args); + break; + case Type::INT32: + indices_hasher = HashInitImpl<UInt32Type, Action>(ctx, args); + break; + case Type::INT64: + indices_hasher = HashInitImpl<UInt64Type, Action>(ctx, args); + break; + default: + DCHECK(false) << "Unsupported dictionary index type"; + break; + } RETURN_NOT_OK(indices_hasher); return ::arrow::internal::make_unique<DictionaryHashKernel>( std::move(indices_hasher.ValueOrDie())); -} - +} + Status HashExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - auto hash_impl = checked_cast<HashKernel*>(ctx->state()); + auto hash_impl = checked_cast<HashKernel*>(ctx->state()); RETURN_NOT_OK(hash_impl->Append(ctx, *batch[0].array())); RETURN_NOT_OK(hash_impl->Flush(out)); return Status::OK(); -} - +} + Status UniqueFinalize(KernelContext* ctx, std::vector<Datum>* out) { - auto hash_impl = checked_cast<HashKernel*>(ctx->state()); - std::shared_ptr<ArrayData> uniques; + auto hash_impl = checked_cast<HashKernel*>(ctx->state()); + std::shared_ptr<ArrayData> uniques; RETURN_NOT_OK(hash_impl->GetDictionary(&uniques)); - *out = {Datum(uniques)}; + *out = {Datum(uniques)}; return Status::OK(); -} - +} + Status DictEncodeFinalize(KernelContext* ctx, std::vector<Datum>* out) { - auto hash_impl = checked_cast<HashKernel*>(ctx->state()); - std::shared_ptr<ArrayData> uniques; + auto hash_impl = checked_cast<HashKernel*>(ctx->state()); + std::shared_ptr<ArrayData> uniques; RETURN_NOT_OK(hash_impl->GetDictionary(&uniques)); - auto dict_type = dictionary(int32(), uniques->type); - auto dict = MakeArray(uniques); - for (size_t i = 0; i < out->size(); ++i) { - (*out)[i] = - std::make_shared<DictionaryArray>(dict_type, (*out)[i].make_array(), dict); - } + auto dict_type = dictionary(int32(), uniques->type); + auto dict = MakeArray(uniques); + for (size_t i = 0; i < out->size(); ++i) { + (*out)[i] = + std::make_shared<DictionaryArray>(dict_type, (*out)[i].make_array(), dict); + } return Status::OK(); -} - -std::shared_ptr<ArrayData> BoxValueCounts(const std::shared_ptr<ArrayData>& uniques, - const std::shared_ptr<ArrayData>& counts) { - auto data_type = - struct_({field(kValuesFieldName, uniques->type), field(kCountsFieldName, int64())}); - ArrayVector children = {MakeArray(uniques), MakeArray(counts)}; - return std::make_shared<StructArray>(data_type, uniques->length, children)->data(); -} - +} + +std::shared_ptr<ArrayData> BoxValueCounts(const std::shared_ptr<ArrayData>& uniques, + const std::shared_ptr<ArrayData>& counts) { + auto data_type = + struct_({field(kValuesFieldName, uniques->type), field(kCountsFieldName, int64())}); + ArrayVector children = {MakeArray(uniques), MakeArray(counts)}; + return std::make_shared<StructArray>(data_type, uniques->length, children)->data(); +} + Status ValueCountsFinalize(KernelContext* ctx, std::vector<Datum>* out) { - auto hash_impl = checked_cast<HashKernel*>(ctx->state()); - std::shared_ptr<ArrayData> uniques; - Datum value_counts; - + auto hash_impl = checked_cast<HashKernel*>(ctx->state()); + std::shared_ptr<ArrayData> uniques; + Datum value_counts; + RETURN_NOT_OK(hash_impl->GetDictionary(&uniques)); RETURN_NOT_OK(hash_impl->FlushFinal(&value_counts)); - *out = {Datum(BoxValueCounts(uniques, value_counts.array()))}; + *out = {Datum(BoxValueCounts(uniques, value_counts.array()))}; return Status::OK(); -} - +} + Status UniqueFinalizeDictionary(KernelContext* ctx, std::vector<Datum>* out) { RETURN_NOT_OK(UniqueFinalize(ctx, out)); - auto hash = checked_cast<DictionaryHashKernel*>(ctx->state()); - (*out)[0].mutable_array()->dictionary = hash->dictionary(); + auto hash = checked_cast<DictionaryHashKernel*>(ctx->state()); + (*out)[0].mutable_array()->dictionary = hash->dictionary(); return Status::OK(); -} - +} + Status ValueCountsFinalizeDictionary(KernelContext* ctx, std::vector<Datum>* out) { - auto hash = checked_cast<DictionaryHashKernel*>(ctx->state()); - std::shared_ptr<ArrayData> uniques; - Datum value_counts; + auto hash = checked_cast<DictionaryHashKernel*>(ctx->state()); + std::shared_ptr<ArrayData> uniques; + Datum value_counts; RETURN_NOT_OK(hash->GetDictionary(&uniques)); RETURN_NOT_OK(hash->FlushFinal(&value_counts)); - uniques->dictionary = hash->dictionary(); - *out = {Datum(BoxValueCounts(uniques, value_counts.array()))}; + uniques->dictionary = hash->dictionary(); + *out = {Datum(BoxValueCounts(uniques, value_counts.array()))}; return Status::OK(); -} - -ValueDescr DictEncodeOutput(KernelContext*, const std::vector<ValueDescr>& descrs) { - return ValueDescr::Array(dictionary(int32(), descrs[0].type)); -} - -ValueDescr ValueCountsOutput(KernelContext*, const std::vector<ValueDescr>& descrs) { - return ValueDescr::Array(struct_( - {field(kValuesFieldName, descrs[0].type), field(kCountsFieldName, int64())})); -} - -template <typename Action> -void AddHashKernels(VectorFunction* func, VectorKernel base, OutputType out_ty) { - for (const auto& ty : PrimitiveTypes()) { - base.init = GetHashInit<Action>(ty->id()); - base.signature = KernelSignature::Make({InputType::Array(ty)}, out_ty); - DCHECK_OK(func->AddKernel(base)); - } - - // Example parametric types that we want to match only on Type::type - auto parametric_types = {time32(TimeUnit::SECOND), time64(TimeUnit::MICRO), - timestamp(TimeUnit::SECOND), fixed_size_binary(0)}; - for (const auto& ty : parametric_types) { - base.init = GetHashInit<Action>(ty->id()); - base.signature = KernelSignature::Make({InputType::Array(ty->id())}, out_ty); - DCHECK_OK(func->AddKernel(base)); - } - +} + +ValueDescr DictEncodeOutput(KernelContext*, const std::vector<ValueDescr>& descrs) { + return ValueDescr::Array(dictionary(int32(), descrs[0].type)); +} + +ValueDescr ValueCountsOutput(KernelContext*, const std::vector<ValueDescr>& descrs) { + return ValueDescr::Array(struct_( + {field(kValuesFieldName, descrs[0].type), field(kCountsFieldName, int64())})); +} + +template <typename Action> +void AddHashKernels(VectorFunction* func, VectorKernel base, OutputType out_ty) { + for (const auto& ty : PrimitiveTypes()) { + base.init = GetHashInit<Action>(ty->id()); + base.signature = KernelSignature::Make({InputType::Array(ty)}, out_ty); + DCHECK_OK(func->AddKernel(base)); + } + + // Example parametric types that we want to match only on Type::type + auto parametric_types = {time32(TimeUnit::SECOND), time64(TimeUnit::MICRO), + timestamp(TimeUnit::SECOND), fixed_size_binary(0)}; + for (const auto& ty : parametric_types) { + base.init = GetHashInit<Action>(ty->id()); + base.signature = KernelSignature::Make({InputType::Array(ty->id())}, out_ty); + DCHECK_OK(func->AddKernel(base)); + } + for (auto t : {Type::DECIMAL128, Type::DECIMAL256}) { base.init = GetHashInit<Action>(t); base.signature = KernelSignature::Make({InputType::Array(t)}, out_ty); DCHECK_OK(func->AddKernel(base)); } -} - +} + const FunctionDoc unique_doc( "Compute unique elements", ("Return an array with distinct values. Nulls in the input are ignored."), @@ -718,65 +718,65 @@ const FunctionDoc dictionary_encode_doc( ("Return a dictionary-encoded version of the input array."), {"array"}, "DictionaryEncodeOptions"); -} // namespace - -void RegisterVectorHash(FunctionRegistry* registry) { - VectorKernel base; - base.exec = HashExec; - - // ---------------------------------------------------------------------- - // unique - - base.finalize = UniqueFinalize; - base.output_chunked = false; +} // namespace + +void RegisterVectorHash(FunctionRegistry* registry) { + VectorKernel base; + base.exec = HashExec; + + // ---------------------------------------------------------------------- + // unique + + base.finalize = UniqueFinalize; + base.output_chunked = false; auto unique = std::make_shared<VectorFunction>("unique", Arity::Unary(), &unique_doc); - AddHashKernels<UniqueAction>(unique.get(), base, OutputType(FirstType)); - - // Dictionary unique - base.init = DictionaryHashInit<UniqueAction>; - base.finalize = UniqueFinalizeDictionary; - base.signature = - KernelSignature::Make({InputType::Array(Type::DICTIONARY)}, OutputType(FirstType)); - DCHECK_OK(unique->AddKernel(base)); - - DCHECK_OK(registry->AddFunction(std::move(unique))); - - // ---------------------------------------------------------------------- - // value_counts - - base.finalize = ValueCountsFinalize; + AddHashKernels<UniqueAction>(unique.get(), base, OutputType(FirstType)); + + // Dictionary unique + base.init = DictionaryHashInit<UniqueAction>; + base.finalize = UniqueFinalizeDictionary; + base.signature = + KernelSignature::Make({InputType::Array(Type::DICTIONARY)}, OutputType(FirstType)); + DCHECK_OK(unique->AddKernel(base)); + + DCHECK_OK(registry->AddFunction(std::move(unique))); + + // ---------------------------------------------------------------------- + // value_counts + + base.finalize = ValueCountsFinalize; auto value_counts = std::make_shared<VectorFunction>("value_counts", Arity::Unary(), &value_counts_doc); - AddHashKernels<ValueCountsAction>(value_counts.get(), base, - OutputType(ValueCountsOutput)); - - // Dictionary value counts - base.init = DictionaryHashInit<ValueCountsAction>; - base.finalize = ValueCountsFinalizeDictionary; - base.signature = KernelSignature::Make({InputType::Array(Type::DICTIONARY)}, - OutputType(ValueCountsOutput)); - DCHECK_OK(value_counts->AddKernel(base)); - - DCHECK_OK(registry->AddFunction(std::move(value_counts))); - - // ---------------------------------------------------------------------- - // dictionary_encode - - base.finalize = DictEncodeFinalize; - // Unique and ValueCounts output unchunked arrays - base.output_chunked = true; + AddHashKernels<ValueCountsAction>(value_counts.get(), base, + OutputType(ValueCountsOutput)); + + // Dictionary value counts + base.init = DictionaryHashInit<ValueCountsAction>; + base.finalize = ValueCountsFinalizeDictionary; + base.signature = KernelSignature::Make({InputType::Array(Type::DICTIONARY)}, + OutputType(ValueCountsOutput)); + DCHECK_OK(value_counts->AddKernel(base)); + + DCHECK_OK(registry->AddFunction(std::move(value_counts))); + + // ---------------------------------------------------------------------- + // dictionary_encode + + base.finalize = DictEncodeFinalize; + // Unique and ValueCounts output unchunked arrays + base.output_chunked = true; auto dict_encode = std::make_shared<VectorFunction>("dictionary_encode", Arity::Unary(), &dictionary_encode_doc, &kDefaultDictionaryEncodeOptions); - AddHashKernels<DictEncodeAction>(dict_encode.get(), base, OutputType(DictEncodeOutput)); - - // Calling dictionary_encode on dictionary input not supported, but if it - // ends up being needed (or convenience), a kernel could be added to make it - // a no-op - - DCHECK_OK(registry->AddFunction(std::move(dict_encode))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow + AddHashKernels<DictEncodeAction>(dict_encode.get(), base, OutputType(DictEncodeOutput)); + + // Calling dictionary_encode on dictionary input not supported, but if it + // ends up being needed (or convenience), a kernel could be added to make it + // a no-op + + DCHECK_OK(registry->AddFunction(std::move(dict_encode))); +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_nested.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_nested.cc index 627f4edf96..b84640854e 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_nested.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_nested.cc @@ -1,68 +1,68 @@ -// 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. - -// Vector kernels involving nested types - -#include "arrow/array/array_base.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/result.h" - -namespace arrow { -namespace compute { -namespace internal { -namespace { - -template <typename Type> +// 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. + +// Vector kernels involving nested types + +#include "arrow/array/array_base.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/result.h" + +namespace arrow { +namespace compute { +namespace internal { +namespace { + +template <typename Type> Status ListFlatten(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - typename TypeTraits<Type>::ArrayType list_array(batch[0].array()); + typename TypeTraits<Type>::ArrayType list_array(batch[0].array()); ARROW_ASSIGN_OR_RAISE(auto result, list_array.Flatten(ctx->memory_pool())); out->value = result->data(); return Status::OK(); -} - -template <typename Type, typename offset_type = typename Type::offset_type> +} + +template <typename Type, typename offset_type = typename Type::offset_type> Status ListParentIndices(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - typename TypeTraits<Type>::ArrayType list(batch[0].array()); - ArrayData* out_arr = out->mutable_array(); - - const offset_type* offsets = list.raw_value_offsets(); - offset_type values_length = offsets[list.length()] - offsets[0]; - - out_arr->length = values_length; - out_arr->null_count = 0; + typename TypeTraits<Type>::ArrayType list(batch[0].array()); + ArrayData* out_arr = out->mutable_array(); + + const offset_type* offsets = list.raw_value_offsets(); + offset_type values_length = offsets[list.length()] - offsets[0]; + + out_arr->length = values_length; + out_arr->null_count = 0; ARROW_ASSIGN_OR_RAISE(out_arr->buffers[1], ctx->Allocate(values_length * sizeof(offset_type))); - auto out_indices = reinterpret_cast<offset_type*>(out_arr->buffers[1]->mutable_data()); - for (int64_t i = 0; i < list.length(); ++i) { - // Note: In most cases, null slots are empty, but when they are non-empty - // we write out the indices so make sure they are accounted for. This - // behavior could be changed if needed in the future. - for (offset_type j = offsets[i]; j < offsets[i + 1]; ++j) { - *out_indices++ = static_cast<offset_type>(i); - } - } + auto out_indices = reinterpret_cast<offset_type*>(out_arr->buffers[1]->mutable_data()); + for (int64_t i = 0; i < list.length(); ++i) { + // Note: In most cases, null slots are empty, but when they are non-empty + // we write out the indices so make sure they are accounted for. This + // behavior could be changed if needed in the future. + for (offset_type j = offsets[i]; j < offsets[i + 1]; ++j) { + *out_indices++ = static_cast<offset_type>(i); + } + } return Status::OK(); -} - -Result<ValueDescr> ValuesType(KernelContext*, const std::vector<ValueDescr>& args) { - const auto& list_type = checked_cast<const BaseListType&>(*args[0].type); - return ValueDescr::Array(list_type.value_type()); -} - +} + +Result<ValueDescr> ValuesType(KernelContext*, const std::vector<ValueDescr>& args) { + const auto& list_type = checked_cast<const BaseListType&>(*args[0].type); + return ValueDescr::Array(list_type.value_type()); +} + const FunctionDoc list_flatten_doc( "Flatten list values", ("`lists` must have a list-like type.\n" @@ -77,26 +77,26 @@ const FunctionDoc list_parent_indices_doc( "is emitted."), {"lists"}); -} // namespace - -void RegisterVectorNested(FunctionRegistry* registry) { +} // namespace + +void RegisterVectorNested(FunctionRegistry* registry) { auto flatten = std::make_shared<VectorFunction>("list_flatten", Arity::Unary(), &list_flatten_doc); - DCHECK_OK(flatten->AddKernel({InputType::Array(Type::LIST)}, OutputType(ValuesType), - ListFlatten<ListType>)); - DCHECK_OK(flatten->AddKernel({InputType::Array(Type::LARGE_LIST)}, - OutputType(ValuesType), ListFlatten<LargeListType>)); - DCHECK_OK(registry->AddFunction(std::move(flatten))); - + DCHECK_OK(flatten->AddKernel({InputType::Array(Type::LIST)}, OutputType(ValuesType), + ListFlatten<ListType>)); + DCHECK_OK(flatten->AddKernel({InputType::Array(Type::LARGE_LIST)}, + OutputType(ValuesType), ListFlatten<LargeListType>)); + DCHECK_OK(registry->AddFunction(std::move(flatten))); + auto list_parent_indices = std::make_shared<VectorFunction>( "list_parent_indices", Arity::Unary(), &list_parent_indices_doc); - DCHECK_OK(list_parent_indices->AddKernel({InputType::Array(Type::LIST)}, int32(), - ListParentIndices<ListType>)); - DCHECK_OK(list_parent_indices->AddKernel({InputType::Array(Type::LARGE_LIST)}, int64(), - ListParentIndices<LargeListType>)); - DCHECK_OK(registry->AddFunction(std::move(list_parent_indices))); -} - -} // namespace internal -} // namespace compute -} // namespace arrow + DCHECK_OK(list_parent_indices->AddKernel({InputType::Array(Type::LIST)}, int32(), + ListParentIndices<ListType>)); + DCHECK_OK(list_parent_indices->AddKernel({InputType::Array(Type::LARGE_LIST)}, int64(), + ListParentIndices<LargeListType>)); + DCHECK_OK(registry->AddFunction(std::move(list_parent_indices))); +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_selection.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_selection.cc index f4fd377eff..5845a7ee2d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_selection.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_selection.cc @@ -1,122 +1,122 @@ -// 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. - -#include <algorithm> -#include <cstring> -#include <limits> - -#include "arrow/array/array_base.h" -#include "arrow/array/array_binary.h" -#include "arrow/array/array_dict.h" -#include "arrow/array/array_nested.h" -#include "arrow/array/builder_primitive.h" -#include "arrow/array/concatenate.h" -#include "arrow/buffer_builder.h" -#include "arrow/chunked_array.h" -#include "arrow/compute/api_vector.h" -#include "arrow/compute/kernels/common.h" -#include "arrow/compute/kernels/util_internal.h" -#include "arrow/extension_type.h" -#include "arrow/record_batch.h" -#include "arrow/result.h" -#include "arrow/table.h" -#include "arrow/type.h" -#include "arrow/util/bit_block_counter.h" +// 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. + +#include <algorithm> +#include <cstring> +#include <limits> + +#include "arrow/array/array_base.h" +#include "arrow/array/array_binary.h" +#include "arrow/array/array_dict.h" +#include "arrow/array/array_nested.h" +#include "arrow/array/builder_primitive.h" +#include "arrow/array/concatenate.h" +#include "arrow/buffer_builder.h" +#include "arrow/chunked_array.h" +#include "arrow/compute/api_vector.h" +#include "arrow/compute/kernels/common.h" +#include "arrow/compute/kernels/util_internal.h" +#include "arrow/extension_type.h" +#include "arrow/record_batch.h" +#include "arrow/result.h" +#include "arrow/table.h" +#include "arrow/type.h" +#include "arrow/util/bit_block_counter.h" #include "arrow/util/bit_run_reader.h" -#include "arrow/util/bit_util.h" -#include "arrow/util/bitmap_ops.h" -#include "arrow/util/bitmap_reader.h" -#include "arrow/util/int_util.h" - -namespace arrow { - -using internal::BinaryBitBlockCounter; -using internal::BitBlockCount; -using internal::BitBlockCounter; -using internal::CheckIndexBounds; -using internal::CopyBitmap; -using internal::CountSetBits; -using internal::GetArrayView; -using internal::GetByteWidth; -using internal::OptionalBitBlockCounter; -using internal::OptionalBitIndexer; - -namespace compute { -namespace internal { - -int64_t GetFilterOutputSize(const ArrayData& filter, - FilterOptions::NullSelectionBehavior null_selection) { - int64_t output_size = 0; - - if (filter.MayHaveNulls()) { - const uint8_t* filter_is_valid = filter.buffers[0]->data(); - BinaryBitBlockCounter bit_counter(filter.buffers[1]->data(), filter.offset, - filter_is_valid, filter.offset, filter.length); - int64_t position = 0; - if (null_selection == FilterOptions::EMIT_NULL) { - while (position < filter.length) { - BitBlockCount block = bit_counter.NextOrNotWord(); - output_size += block.popcount; - position += block.length; - } - } else { - while (position < filter.length) { - BitBlockCount block = bit_counter.NextAndWord(); - output_size += block.popcount; - position += block.length; - } - } - } else { - // The filter has no nulls, so we can use CountSetBits - output_size = CountSetBits(filter.buffers[1]->data(), filter.offset, filter.length); - } - return output_size; -} - +#include "arrow/util/bit_util.h" +#include "arrow/util/bitmap_ops.h" +#include "arrow/util/bitmap_reader.h" +#include "arrow/util/int_util.h" + +namespace arrow { + +using internal::BinaryBitBlockCounter; +using internal::BitBlockCount; +using internal::BitBlockCounter; +using internal::CheckIndexBounds; +using internal::CopyBitmap; +using internal::CountSetBits; +using internal::GetArrayView; +using internal::GetByteWidth; +using internal::OptionalBitBlockCounter; +using internal::OptionalBitIndexer; + +namespace compute { +namespace internal { + +int64_t GetFilterOutputSize(const ArrayData& filter, + FilterOptions::NullSelectionBehavior null_selection) { + int64_t output_size = 0; + + if (filter.MayHaveNulls()) { + const uint8_t* filter_is_valid = filter.buffers[0]->data(); + BinaryBitBlockCounter bit_counter(filter.buffers[1]->data(), filter.offset, + filter_is_valid, filter.offset, filter.length); + int64_t position = 0; + if (null_selection == FilterOptions::EMIT_NULL) { + while (position < filter.length) { + BitBlockCount block = bit_counter.NextOrNotWord(); + output_size += block.popcount; + position += block.length; + } + } else { + while (position < filter.length) { + BitBlockCount block = bit_counter.NextAndWord(); + output_size += block.popcount; + position += block.length; + } + } + } else { + // The filter has no nulls, so we can use CountSetBits + output_size = CountSetBits(filter.buffers[1]->data(), filter.offset, filter.length); + } + return output_size; +} + namespace { -template <typename IndexType> -Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( - const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, - MemoryPool* memory_pool) { - using T = typename IndexType::c_type; - - const uint8_t* filter_data = filter.buffers[1]->data(); +template <typename IndexType> +Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( + const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, + MemoryPool* memory_pool) { + using T = typename IndexType::c_type; + + const uint8_t* filter_data = filter.buffers[1]->data(); const bool have_filter_nulls = filter.MayHaveNulls(); const uint8_t* filter_is_valid = have_filter_nulls ? filter.buffers[0]->data() : nullptr; - + if (have_filter_nulls && null_selection == FilterOptions::EMIT_NULL) { // Most complex case: the filter may have nulls and we don't drop them. // The logic is ternary: // - filter is null: emit null // - filter is valid and true: emit index // - filter is valid and false: don't emit anything - + typename TypeTraits<IndexType>::BuilderType builder(memory_pool); - + // The position relative to the start of the filter T position = 0; // The current position taking the filter offset into account int64_t position_with_offset = filter.offset; // To count blocks where filter_data[i] || !filter_is_valid[i] - BinaryBitBlockCounter filter_counter(filter_data, filter.offset, filter_is_valid, - filter.offset, filter.length); + BinaryBitBlockCounter filter_counter(filter_data, filter.offset, filter_is_valid, + filter.offset, filter.length); BitBlockCounter is_valid_counter(filter_is_valid, filter.offset, filter.length); while (position < filter.length) { // true OR NOT valid @@ -125,13 +125,13 @@ Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( position += selected_or_null_block.length; position_with_offset += selected_or_null_block.length; continue; - } + } RETURN_NOT_OK(builder.Reserve(selected_or_null_block.popcount)); - + // If the values are all valid and the selected_or_null_block is full, // then we can infer that all the values are true and skip the bit checking BitBlockCount is_valid_block = is_valid_counter.NextWord(); - + if (selected_or_null_block.AllSet() && is_valid_block.AllSet()) { // All the values are selected and non-null for (int64_t i = 0; i < selected_or_null_block.length; ++i) { @@ -144,24 +144,24 @@ Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( if (BitUtil::GetBit(filter_is_valid, position_with_offset)) { if (BitUtil::GetBit(filter_data, position_with_offset)) { builder.UnsafeAppend(position); - } + } } else { // Null slot, so append a null builder.UnsafeAppendNull(); - } + } ++position; ++position_with_offset; - } - } - } + } + } + } std::shared_ptr<ArrayData> result; RETURN_NOT_OK(builder.FinishInternal(&result)); return result; } - + // Other cases don't emit nulls and are therefore simpler. TypedBufferBuilder<T> builder(memory_pool); - + if (have_filter_nulls) { // The filter may have nulls, so we scan the validity bitmap and the filter // data bitmap together. @@ -180,24 +180,24 @@ Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( if (and_block.AllSet()) { // All the values are selected and non-null for (int64_t i = 0; i < and_block.length; ++i) { - builder.UnsafeAppend(position++); - } + builder.UnsafeAppend(position++); + } position_with_offset += and_block.length; } else if (!and_block.NoneSet()) { // Some of the values are false or null for (int64_t i = 0; i < and_block.length; ++i) { if (BitUtil::GetBit(filter_is_valid, position_with_offset) && BitUtil::GetBit(filter_data, position_with_offset)) { - builder.UnsafeAppend(position); - } - ++position; - ++position_with_offset; - } - } else { + builder.UnsafeAppend(position); + } + ++position; + ++position_with_offset; + } + } else { position += and_block.length; position_with_offset += and_block.length; - } - } + } + } } else { // The filter has no nulls, so we need only look for true values RETURN_NOT_OK(::arrow::internal::VisitSetBitRuns( @@ -209,1465 +209,1465 @@ Result<std::shared_ptr<ArrayData>> GetTakeIndicesImpl( } return Status::OK(); })); - } + } const int64_t length = builder.length(); std::shared_ptr<Buffer> out_buffer; RETURN_NOT_OK(builder.Finish(&out_buffer)); return std::make_shared<ArrayData>(TypeTraits<IndexType>::type_singleton(), length, BufferVector{nullptr, out_buffer}, /*null_count=*/0); -} - +} + } // namespace -Result<std::shared_ptr<ArrayData>> GetTakeIndices( - const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, - MemoryPool* memory_pool) { - DCHECK_EQ(filter.type->id(), Type::BOOL); - if (filter.length <= std::numeric_limits<uint16_t>::max()) { - return GetTakeIndicesImpl<UInt16Type>(filter, null_selection, memory_pool); - } else if (filter.length <= std::numeric_limits<uint32_t>::max()) { - return GetTakeIndicesImpl<UInt32Type>(filter, null_selection, memory_pool); - } else { - // Arrays over 4 billion elements, not especially likely. - return Status::NotImplemented( - "Filter length exceeds UINT32_MAX, " - "consider a different strategy for selecting elements"); - } -} - -namespace { - -using FilterState = OptionsWrapper<FilterOptions>; -using TakeState = OptionsWrapper<TakeOptions>; - -Status PreallocateData(KernelContext* ctx, int64_t length, int bit_width, - bool allocate_validity, ArrayData* out) { - // Preallocate memory - out->length = length; - out->buffers.resize(2); - - if (allocate_validity) { - ARROW_ASSIGN_OR_RAISE(out->buffers[0], ctx->AllocateBitmap(length)); - } - if (bit_width == 1) { - ARROW_ASSIGN_OR_RAISE(out->buffers[1], ctx->AllocateBitmap(length)); - } else { - ARROW_ASSIGN_OR_RAISE(out->buffers[1], ctx->Allocate(length * bit_width / 8)); - } - return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Implement optimized take for primitive types from boolean to 1/2/4/8-byte -// C-type based types. Use common implementation for every byte width and only -// generate code for unsigned integer indices, since after boundschecking to -// check for negative numbers in the indices we can safely reinterpret_cast -// signed integers as unsigned. - -/// \brief The Take implementation for primitive (fixed-width) types does not -/// use the logical Arrow type but rather the physical C type. This way we -/// only generate one take function for each byte width. -/// -/// This function assumes that the indices have been boundschecked. -template <typename IndexCType, typename ValueCType> -struct PrimitiveTakeImpl { - static void Exec(const PrimitiveArg& values, const PrimitiveArg& indices, - ArrayData* out_arr) { - auto values_data = reinterpret_cast<const ValueCType*>(values.data); - auto values_is_valid = values.is_valid; - auto values_offset = values.offset; - - auto indices_data = reinterpret_cast<const IndexCType*>(indices.data); - auto indices_is_valid = indices.is_valid; - auto indices_offset = indices.offset; - - auto out = out_arr->GetMutableValues<ValueCType>(1); - auto out_is_valid = out_arr->buffers[0]->mutable_data(); - auto out_offset = out_arr->offset; - - // If either the values or indices have nulls, we preemptively zero out the - // out validity bitmap so that we don't have to use ClearBit in each - // iteration for nulls. - if (values.null_count != 0 || indices.null_count != 0) { - BitUtil::SetBitsTo(out_is_valid, out_offset, indices.length, false); - } - - OptionalBitBlockCounter indices_bit_counter(indices_is_valid, indices_offset, - indices.length); - int64_t position = 0; - int64_t valid_count = 0; - while (position < indices.length) { - BitBlockCount block = indices_bit_counter.NextBlock(); - if (values.null_count == 0) { - // Values are never null, so things are easier - valid_count += block.popcount; - if (block.popcount == block.length) { - // Fastest path: neither values nor index nulls - BitUtil::SetBitsTo(out_is_valid, out_offset + position, block.length, true); - for (int64_t i = 0; i < block.length; ++i) { - out[position] = values_data[indices_data[position]]; - ++position; - } - } else if (block.popcount > 0) { - // Slow path: some indices but not all are null - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { - // index is not null - BitUtil::SetBit(out_is_valid, out_offset + position); - out[position] = values_data[indices_data[position]]; - } else { - out[position] = ValueCType{}; - } - ++position; - } - } else { - memset(out + position, 0, sizeof(ValueCType) * block.length); - position += block.length; - } - } else { - // Values have nulls, so we must do random access into the values bitmap - if (block.popcount == block.length) { - // Faster path: indices are not null but values may be - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(values_is_valid, - values_offset + indices_data[position])) { - // value is not null - out[position] = values_data[indices_data[position]]; - BitUtil::SetBit(out_is_valid, out_offset + position); - ++valid_count; - } else { - out[position] = ValueCType{}; - } - ++position; - } - } else if (block.popcount > 0) { - // Slow path: some but not all indices are null. Since we are doing - // random access in general we have to check the value nullness one by - // one. - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(indices_is_valid, indices_offset + position) && - BitUtil::GetBit(values_is_valid, - values_offset + indices_data[position])) { - // index is not null && value is not null - out[position] = values_data[indices_data[position]]; - BitUtil::SetBit(out_is_valid, out_offset + position); - ++valid_count; - } else { - out[position] = ValueCType{}; - } - ++position; - } - } else { - memset(out + position, 0, sizeof(ValueCType) * block.length); - position += block.length; - } - } - } - out_arr->null_count = out_arr->length - valid_count; - } -}; - -template <typename IndexCType> -struct BooleanTakeImpl { - static void Exec(const PrimitiveArg& values, const PrimitiveArg& indices, - ArrayData* out_arr) { - const uint8_t* values_data = values.data; - auto values_is_valid = values.is_valid; - auto values_offset = values.offset; - - auto indices_data = reinterpret_cast<const IndexCType*>(indices.data); - auto indices_is_valid = indices.is_valid; - auto indices_offset = indices.offset; - - auto out = out_arr->buffers[1]->mutable_data(); - auto out_is_valid = out_arr->buffers[0]->mutable_data(); - auto out_offset = out_arr->offset; - - // If either the values or indices have nulls, we preemptively zero out the - // out validity bitmap so that we don't have to use ClearBit in each - // iteration for nulls. - if (values.null_count != 0 || indices.null_count != 0) { - BitUtil::SetBitsTo(out_is_valid, out_offset, indices.length, false); - } - // Avoid uninitialized data in values array - BitUtil::SetBitsTo(out, out_offset, indices.length, false); - - auto PlaceDataBit = [&](int64_t loc, IndexCType index) { - BitUtil::SetBitTo(out, out_offset + loc, - BitUtil::GetBit(values_data, values_offset + index)); - }; - - OptionalBitBlockCounter indices_bit_counter(indices_is_valid, indices_offset, - indices.length); - int64_t position = 0; - int64_t valid_count = 0; - while (position < indices.length) { - BitBlockCount block = indices_bit_counter.NextBlock(); - if (values.null_count == 0) { - // Values are never null, so things are easier - valid_count += block.popcount; - if (block.popcount == block.length) { - // Fastest path: neither values nor index nulls - BitUtil::SetBitsTo(out_is_valid, out_offset + position, block.length, true); - for (int64_t i = 0; i < block.length; ++i) { - PlaceDataBit(position, indices_data[position]); - ++position; - } - } else if (block.popcount > 0) { - // Slow path: some but not all indices are null - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { - // index is not null - BitUtil::SetBit(out_is_valid, out_offset + position); - PlaceDataBit(position, indices_data[position]); - } - ++position; - } - } else { - position += block.length; - } - } else { - // Values have nulls, so we must do random access into the values bitmap - if (block.popcount == block.length) { - // Faster path: indices are not null but values may be - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(values_is_valid, - values_offset + indices_data[position])) { - // value is not null - BitUtil::SetBit(out_is_valid, out_offset + position); - PlaceDataBit(position, indices_data[position]); - ++valid_count; - } - ++position; - } - } else if (block.popcount > 0) { - // Slow path: some but not all indices are null. Since we are doing - // random access in general we have to check the value nullness one by - // one. - for (int64_t i = 0; i < block.length; ++i) { - if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { - // index is not null - if (BitUtil::GetBit(values_is_valid, - values_offset + indices_data[position])) { - // value is not null - PlaceDataBit(position, indices_data[position]); - BitUtil::SetBit(out_is_valid, out_offset + position); - ++valid_count; - } - } - ++position; - } - } else { - position += block.length; - } - } - } - out_arr->null_count = out_arr->length - valid_count; - } -}; - -template <template <typename...> class TakeImpl, typename... Args> -void TakeIndexDispatch(const PrimitiveArg& values, const PrimitiveArg& indices, - ArrayData* out) { - // With the simplifying assumption that boundschecking has taken place - // already at a higher level, we can now assume that the index values are all - // non-negative. Thus, we can interpret signed integers as unsigned and avoid - // having to generate double the amount of binary code to handle each integer - // width. - switch (indices.bit_width) { - case 8: - return TakeImpl<uint8_t, Args...>::Exec(values, indices, out); - case 16: - return TakeImpl<uint16_t, Args...>::Exec(values, indices, out); - case 32: - return TakeImpl<uint32_t, Args...>::Exec(values, indices, out); - case 64: - return TakeImpl<uint64_t, Args...>::Exec(values, indices, out); - default: - DCHECK(false) << "Invalid indices byte width"; - break; - } -} - +Result<std::shared_ptr<ArrayData>> GetTakeIndices( + const ArrayData& filter, FilterOptions::NullSelectionBehavior null_selection, + MemoryPool* memory_pool) { + DCHECK_EQ(filter.type->id(), Type::BOOL); + if (filter.length <= std::numeric_limits<uint16_t>::max()) { + return GetTakeIndicesImpl<UInt16Type>(filter, null_selection, memory_pool); + } else if (filter.length <= std::numeric_limits<uint32_t>::max()) { + return GetTakeIndicesImpl<UInt32Type>(filter, null_selection, memory_pool); + } else { + // Arrays over 4 billion elements, not especially likely. + return Status::NotImplemented( + "Filter length exceeds UINT32_MAX, " + "consider a different strategy for selecting elements"); + } +} + +namespace { + +using FilterState = OptionsWrapper<FilterOptions>; +using TakeState = OptionsWrapper<TakeOptions>; + +Status PreallocateData(KernelContext* ctx, int64_t length, int bit_width, + bool allocate_validity, ArrayData* out) { + // Preallocate memory + out->length = length; + out->buffers.resize(2); + + if (allocate_validity) { + ARROW_ASSIGN_OR_RAISE(out->buffers[0], ctx->AllocateBitmap(length)); + } + if (bit_width == 1) { + ARROW_ASSIGN_OR_RAISE(out->buffers[1], ctx->AllocateBitmap(length)); + } else { + ARROW_ASSIGN_OR_RAISE(out->buffers[1], ctx->Allocate(length * bit_width / 8)); + } + return Status::OK(); +} + +// ---------------------------------------------------------------------- +// Implement optimized take for primitive types from boolean to 1/2/4/8-byte +// C-type based types. Use common implementation for every byte width and only +// generate code for unsigned integer indices, since after boundschecking to +// check for negative numbers in the indices we can safely reinterpret_cast +// signed integers as unsigned. + +/// \brief The Take implementation for primitive (fixed-width) types does not +/// use the logical Arrow type but rather the physical C type. This way we +/// only generate one take function for each byte width. +/// +/// This function assumes that the indices have been boundschecked. +template <typename IndexCType, typename ValueCType> +struct PrimitiveTakeImpl { + static void Exec(const PrimitiveArg& values, const PrimitiveArg& indices, + ArrayData* out_arr) { + auto values_data = reinterpret_cast<const ValueCType*>(values.data); + auto values_is_valid = values.is_valid; + auto values_offset = values.offset; + + auto indices_data = reinterpret_cast<const IndexCType*>(indices.data); + auto indices_is_valid = indices.is_valid; + auto indices_offset = indices.offset; + + auto out = out_arr->GetMutableValues<ValueCType>(1); + auto out_is_valid = out_arr->buffers[0]->mutable_data(); + auto out_offset = out_arr->offset; + + // If either the values or indices have nulls, we preemptively zero out the + // out validity bitmap so that we don't have to use ClearBit in each + // iteration for nulls. + if (values.null_count != 0 || indices.null_count != 0) { + BitUtil::SetBitsTo(out_is_valid, out_offset, indices.length, false); + } + + OptionalBitBlockCounter indices_bit_counter(indices_is_valid, indices_offset, + indices.length); + int64_t position = 0; + int64_t valid_count = 0; + while (position < indices.length) { + BitBlockCount block = indices_bit_counter.NextBlock(); + if (values.null_count == 0) { + // Values are never null, so things are easier + valid_count += block.popcount; + if (block.popcount == block.length) { + // Fastest path: neither values nor index nulls + BitUtil::SetBitsTo(out_is_valid, out_offset + position, block.length, true); + for (int64_t i = 0; i < block.length; ++i) { + out[position] = values_data[indices_data[position]]; + ++position; + } + } else if (block.popcount > 0) { + // Slow path: some indices but not all are null + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { + // index is not null + BitUtil::SetBit(out_is_valid, out_offset + position); + out[position] = values_data[indices_data[position]]; + } else { + out[position] = ValueCType{}; + } + ++position; + } + } else { + memset(out + position, 0, sizeof(ValueCType) * block.length); + position += block.length; + } + } else { + // Values have nulls, so we must do random access into the values bitmap + if (block.popcount == block.length) { + // Faster path: indices are not null but values may be + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(values_is_valid, + values_offset + indices_data[position])) { + // value is not null + out[position] = values_data[indices_data[position]]; + BitUtil::SetBit(out_is_valid, out_offset + position); + ++valid_count; + } else { + out[position] = ValueCType{}; + } + ++position; + } + } else if (block.popcount > 0) { + // Slow path: some but not all indices are null. Since we are doing + // random access in general we have to check the value nullness one by + // one. + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(indices_is_valid, indices_offset + position) && + BitUtil::GetBit(values_is_valid, + values_offset + indices_data[position])) { + // index is not null && value is not null + out[position] = values_data[indices_data[position]]; + BitUtil::SetBit(out_is_valid, out_offset + position); + ++valid_count; + } else { + out[position] = ValueCType{}; + } + ++position; + } + } else { + memset(out + position, 0, sizeof(ValueCType) * block.length); + position += block.length; + } + } + } + out_arr->null_count = out_arr->length - valid_count; + } +}; + +template <typename IndexCType> +struct BooleanTakeImpl { + static void Exec(const PrimitiveArg& values, const PrimitiveArg& indices, + ArrayData* out_arr) { + const uint8_t* values_data = values.data; + auto values_is_valid = values.is_valid; + auto values_offset = values.offset; + + auto indices_data = reinterpret_cast<const IndexCType*>(indices.data); + auto indices_is_valid = indices.is_valid; + auto indices_offset = indices.offset; + + auto out = out_arr->buffers[1]->mutable_data(); + auto out_is_valid = out_arr->buffers[0]->mutable_data(); + auto out_offset = out_arr->offset; + + // If either the values or indices have nulls, we preemptively zero out the + // out validity bitmap so that we don't have to use ClearBit in each + // iteration for nulls. + if (values.null_count != 0 || indices.null_count != 0) { + BitUtil::SetBitsTo(out_is_valid, out_offset, indices.length, false); + } + // Avoid uninitialized data in values array + BitUtil::SetBitsTo(out, out_offset, indices.length, false); + + auto PlaceDataBit = [&](int64_t loc, IndexCType index) { + BitUtil::SetBitTo(out, out_offset + loc, + BitUtil::GetBit(values_data, values_offset + index)); + }; + + OptionalBitBlockCounter indices_bit_counter(indices_is_valid, indices_offset, + indices.length); + int64_t position = 0; + int64_t valid_count = 0; + while (position < indices.length) { + BitBlockCount block = indices_bit_counter.NextBlock(); + if (values.null_count == 0) { + // Values are never null, so things are easier + valid_count += block.popcount; + if (block.popcount == block.length) { + // Fastest path: neither values nor index nulls + BitUtil::SetBitsTo(out_is_valid, out_offset + position, block.length, true); + for (int64_t i = 0; i < block.length; ++i) { + PlaceDataBit(position, indices_data[position]); + ++position; + } + } else if (block.popcount > 0) { + // Slow path: some but not all indices are null + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { + // index is not null + BitUtil::SetBit(out_is_valid, out_offset + position); + PlaceDataBit(position, indices_data[position]); + } + ++position; + } + } else { + position += block.length; + } + } else { + // Values have nulls, so we must do random access into the values bitmap + if (block.popcount == block.length) { + // Faster path: indices are not null but values may be + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(values_is_valid, + values_offset + indices_data[position])) { + // value is not null + BitUtil::SetBit(out_is_valid, out_offset + position); + PlaceDataBit(position, indices_data[position]); + ++valid_count; + } + ++position; + } + } else if (block.popcount > 0) { + // Slow path: some but not all indices are null. Since we are doing + // random access in general we have to check the value nullness one by + // one. + for (int64_t i = 0; i < block.length; ++i) { + if (BitUtil::GetBit(indices_is_valid, indices_offset + position)) { + // index is not null + if (BitUtil::GetBit(values_is_valid, + values_offset + indices_data[position])) { + // value is not null + PlaceDataBit(position, indices_data[position]); + BitUtil::SetBit(out_is_valid, out_offset + position); + ++valid_count; + } + } + ++position; + } + } else { + position += block.length; + } + } + } + out_arr->null_count = out_arr->length - valid_count; + } +}; + +template <template <typename...> class TakeImpl, typename... Args> +void TakeIndexDispatch(const PrimitiveArg& values, const PrimitiveArg& indices, + ArrayData* out) { + // With the simplifying assumption that boundschecking has taken place + // already at a higher level, we can now assume that the index values are all + // non-negative. Thus, we can interpret signed integers as unsigned and avoid + // having to generate double the amount of binary code to handle each integer + // width. + switch (indices.bit_width) { + case 8: + return TakeImpl<uint8_t, Args...>::Exec(values, indices, out); + case 16: + return TakeImpl<uint16_t, Args...>::Exec(values, indices, out); + case 32: + return TakeImpl<uint32_t, Args...>::Exec(values, indices, out); + case 64: + return TakeImpl<uint64_t, Args...>::Exec(values, indices, out); + default: + DCHECK(false) << "Invalid indices byte width"; + break; + } +} + Status PrimitiveTake(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (TakeState::Get(ctx).boundscheck) { + if (TakeState::Get(ctx).boundscheck) { RETURN_NOT_OK(CheckIndexBounds(*batch[1].array(), batch[0].length())); - } - - PrimitiveArg values = GetPrimitiveArg(*batch[0].array()); - PrimitiveArg indices = GetPrimitiveArg(*batch[1].array()); - - ArrayData* out_arr = out->mutable_array(); - - // TODO: When neither values nor indices contain nulls, we can skip - // allocating the validity bitmap altogether and save time and space. A - // streamlined PrimitiveTakeImpl would need to be written that skips all - // interactions with the output validity bitmap, though. + } + + PrimitiveArg values = GetPrimitiveArg(*batch[0].array()); + PrimitiveArg indices = GetPrimitiveArg(*batch[1].array()); + + ArrayData* out_arr = out->mutable_array(); + + // TODO: When neither values nor indices contain nulls, we can skip + // allocating the validity bitmap altogether and save time and space. A + // streamlined PrimitiveTakeImpl would need to be written that skips all + // interactions with the output validity bitmap, though. RETURN_NOT_OK(PreallocateData(ctx, indices.length, values.bit_width, /*allocate_validity=*/true, out_arr)); - switch (values.bit_width) { - case 1: + switch (values.bit_width) { + case 1: TakeIndexDispatch<BooleanTakeImpl>(values, indices, out_arr); break; - case 8: + case 8: TakeIndexDispatch<PrimitiveTakeImpl, int8_t>(values, indices, out_arr); break; - case 16: + case 16: TakeIndexDispatch<PrimitiveTakeImpl, int16_t>(values, indices, out_arr); break; - case 32: + case 32: TakeIndexDispatch<PrimitiveTakeImpl, int32_t>(values, indices, out_arr); break; - case 64: + case 64: TakeIndexDispatch<PrimitiveTakeImpl, int64_t>(values, indices, out_arr); break; - default: - DCHECK(false) << "Invalid values byte width"; - break; - } + default: + DCHECK(false) << "Invalid values byte width"; + break; + } return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Optimized and streamlined filter for primitive types - -// Use either BitBlockCounter or BinaryBitBlockCounter to quickly scan filter a -// word at a time for the DROP selection type. -class DropNullCounter { - public: - // validity bitmap may be null - DropNullCounter(const uint8_t* validity, const uint8_t* data, int64_t offset, - int64_t length) - : data_counter_(data, offset, length), - data_and_validity_counter_(data, offset, validity, offset, length), - has_validity_(validity != nullptr) {} - - BitBlockCount NextBlock() { - if (has_validity_) { - // filter is true AND not null - return data_and_validity_counter_.NextAndWord(); - } else { - return data_counter_.NextWord(); - } - } - - private: - // For when just data is present, but no validity bitmap - BitBlockCounter data_counter_; - - // For when both validity bitmap and data are present - BinaryBitBlockCounter data_and_validity_counter_; - const bool has_validity_; -}; - -/// \brief The Filter implementation for primitive (fixed-width) types does not -/// use the logical Arrow type but rather the physical C type. This way we only -/// generate one take function for each byte width. We use the same -/// implementation here for boolean and fixed-byte-size inputs with some -/// template specialization. -template <typename ArrowType> -class PrimitiveFilterImpl { - public: - using T = typename std::conditional<std::is_same<ArrowType, BooleanType>::value, - uint8_t, typename ArrowType::c_type>::type; - - PrimitiveFilterImpl(const PrimitiveArg& values, const PrimitiveArg& filter, - FilterOptions::NullSelectionBehavior null_selection, - ArrayData* out_arr) - : values_is_valid_(values.is_valid), - values_data_(reinterpret_cast<const T*>(values.data)), - values_null_count_(values.null_count), - values_offset_(values.offset), - values_length_(values.length), - filter_is_valid_(filter.is_valid), - filter_data_(filter.data), - filter_null_count_(filter.null_count), - filter_offset_(filter.offset), - null_selection_(null_selection) { - if (out_arr->buffers[0] != nullptr) { - // May not be allocated if neither filter nor values contains nulls - out_is_valid_ = out_arr->buffers[0]->mutable_data(); - } - out_data_ = reinterpret_cast<T*>(out_arr->buffers[1]->mutable_data()); - out_offset_ = out_arr->offset; - out_length_ = out_arr->length; - out_position_ = 0; - } - - void ExecNonNull() { - // Fast filter when values and filter are not null +} + +// ---------------------------------------------------------------------- +// Optimized and streamlined filter for primitive types + +// Use either BitBlockCounter or BinaryBitBlockCounter to quickly scan filter a +// word at a time for the DROP selection type. +class DropNullCounter { + public: + // validity bitmap may be null + DropNullCounter(const uint8_t* validity, const uint8_t* data, int64_t offset, + int64_t length) + : data_counter_(data, offset, length), + data_and_validity_counter_(data, offset, validity, offset, length), + has_validity_(validity != nullptr) {} + + BitBlockCount NextBlock() { + if (has_validity_) { + // filter is true AND not null + return data_and_validity_counter_.NextAndWord(); + } else { + return data_counter_.NextWord(); + } + } + + private: + // For when just data is present, but no validity bitmap + BitBlockCounter data_counter_; + + // For when both validity bitmap and data are present + BinaryBitBlockCounter data_and_validity_counter_; + const bool has_validity_; +}; + +/// \brief The Filter implementation for primitive (fixed-width) types does not +/// use the logical Arrow type but rather the physical C type. This way we only +/// generate one take function for each byte width. We use the same +/// implementation here for boolean and fixed-byte-size inputs with some +/// template specialization. +template <typename ArrowType> +class PrimitiveFilterImpl { + public: + using T = typename std::conditional<std::is_same<ArrowType, BooleanType>::value, + uint8_t, typename ArrowType::c_type>::type; + + PrimitiveFilterImpl(const PrimitiveArg& values, const PrimitiveArg& filter, + FilterOptions::NullSelectionBehavior null_selection, + ArrayData* out_arr) + : values_is_valid_(values.is_valid), + values_data_(reinterpret_cast<const T*>(values.data)), + values_null_count_(values.null_count), + values_offset_(values.offset), + values_length_(values.length), + filter_is_valid_(filter.is_valid), + filter_data_(filter.data), + filter_null_count_(filter.null_count), + filter_offset_(filter.offset), + null_selection_(null_selection) { + if (out_arr->buffers[0] != nullptr) { + // May not be allocated if neither filter nor values contains nulls + out_is_valid_ = out_arr->buffers[0]->mutable_data(); + } + out_data_ = reinterpret_cast<T*>(out_arr->buffers[1]->mutable_data()); + out_offset_ = out_arr->offset; + out_length_ = out_arr->length; + out_position_ = 0; + } + + void ExecNonNull() { + // Fast filter when values and filter are not null ::arrow::internal::VisitSetBitRunsVoid( filter_data_, filter_offset_, values_length_, [&](int64_t position, int64_t length) { WriteValueSegment(position, length); }); - } - - void Exec() { - if (filter_null_count_ == 0 && values_null_count_ == 0) { - return ExecNonNull(); - } - - // Bit counters used for both null_selection behaviors - DropNullCounter drop_null_counter(filter_is_valid_, filter_data_, filter_offset_, - values_length_); - OptionalBitBlockCounter data_counter(values_is_valid_, values_offset_, - values_length_); - OptionalBitBlockCounter filter_valid_counter(filter_is_valid_, filter_offset_, - values_length_); - - auto WriteNotNull = [&](int64_t index) { - BitUtil::SetBit(out_is_valid_, out_offset_ + out_position_); - // Increments out_position_ - WriteValue(index); - }; - - auto WriteMaybeNull = [&](int64_t index) { - BitUtil::SetBitTo(out_is_valid_, out_offset_ + out_position_, - BitUtil::GetBit(values_is_valid_, values_offset_ + index)); - // Increments out_position_ - WriteValue(index); - }; - - int64_t in_position = 0; - while (in_position < values_length_) { - BitBlockCount filter_block = drop_null_counter.NextBlock(); - BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); - BitBlockCount data_block = data_counter.NextWord(); - if (filter_block.AllSet() && data_block.AllSet()) { - // Fastest path: all values in block are included and not null - BitUtil::SetBitsTo(out_is_valid_, out_offset_ + out_position_, - filter_block.length, true); - WriteValueSegment(in_position, filter_block.length); - in_position += filter_block.length; - } else if (filter_block.AllSet()) { - // Faster: all values are selected, but some values are null - // Batch copy bits from values validity bitmap to output validity bitmap - CopyBitmap(values_is_valid_, values_offset_ + in_position, filter_block.length, - out_is_valid_, out_offset_ + out_position_); - WriteValueSegment(in_position, filter_block.length); - in_position += filter_block.length; - } else if (filter_block.NoneSet() && null_selection_ == FilterOptions::DROP) { - // For this exceedingly common case in low-selectivity filters we can - // skip further analysis of the data and move on to the next block. - in_position += filter_block.length; - } else { - // Some filter values are false or null - if (data_block.AllSet()) { - // No values are null - if (filter_valid_block.AllSet()) { - // Filter is non-null but some values are false - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - WriteNotNull(in_position); - } - ++in_position; - } - } else if (null_selection_ == FilterOptions::DROP) { - // If any values are selected, they ARE NOT null - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position) && - BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - WriteNotNull(in_position); - } - ++in_position; - } - } else { // null_selection == FilterOptions::EMIT_NULL - // Data values in this block are not null - for (int64_t i = 0; i < filter_block.length; ++i) { - const bool is_valid = - BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position); - if (is_valid && - BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - // Filter slot is non-null and set - WriteNotNull(in_position); - } else if (!is_valid) { - // Filter slot is null, so we have a null in the output - BitUtil::ClearBit(out_is_valid_, out_offset_ + out_position_); - WriteNull(); - } - ++in_position; - } - } - } else { // !data_block.AllSet() - // Some values are null - if (filter_valid_block.AllSet()) { - // Filter is non-null but some values are false - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - WriteMaybeNull(in_position); - } - ++in_position; - } - } else if (null_selection_ == FilterOptions::DROP) { - // If any values are selected, they ARE NOT null - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position) && - BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - WriteMaybeNull(in_position); - } - ++in_position; - } - } else { // null_selection == FilterOptions::EMIT_NULL - // Data values in this block are not null - for (int64_t i = 0; i < filter_block.length; ++i) { - const bool is_valid = - BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position); - if (is_valid && - BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { - // Filter slot is non-null and set - WriteMaybeNull(in_position); - } else if (!is_valid) { - // Filter slot is null, so we have a null in the output - BitUtil::ClearBit(out_is_valid_, out_offset_ + out_position_); - WriteNull(); - } - ++in_position; - } - } - } - } // !filter_block.AllSet() - } // while(in_position < values_length_) - } - - // Write the next out_position given the selected in_position for the input - // data and advance out_position - void WriteValue(int64_t in_position) { - out_data_[out_position_++] = values_data_[in_position]; - } - - void WriteValueSegment(int64_t in_start, int64_t length) { - std::memcpy(out_data_ + out_position_, values_data_ + in_start, length * sizeof(T)); - out_position_ += length; - } - - void WriteNull() { - // Zero the memory - out_data_[out_position_++] = T{}; - } - - private: - const uint8_t* values_is_valid_; - const T* values_data_; - int64_t values_null_count_; - int64_t values_offset_; - int64_t values_length_; - const uint8_t* filter_is_valid_; - const uint8_t* filter_data_; - int64_t filter_null_count_; - int64_t filter_offset_; - FilterOptions::NullSelectionBehavior null_selection_; - uint8_t* out_is_valid_; - T* out_data_; - int64_t out_offset_; - int64_t out_length_; - int64_t out_position_; -}; - -template <> -inline void PrimitiveFilterImpl<BooleanType>::WriteValue(int64_t in_position) { - BitUtil::SetBitTo(out_data_, out_offset_ + out_position_++, - BitUtil::GetBit(values_data_, values_offset_ + in_position)); -} - -template <> -inline void PrimitiveFilterImpl<BooleanType>::WriteValueSegment(int64_t in_start, - int64_t length) { - CopyBitmap(values_data_, values_offset_ + in_start, length, out_data_, - out_offset_ + out_position_); - out_position_ += length; -} - -template <> -inline void PrimitiveFilterImpl<BooleanType>::WriteNull() { - // Zero the bit - BitUtil::ClearBit(out_data_, out_offset_ + out_position_++); -} - + } + + void Exec() { + if (filter_null_count_ == 0 && values_null_count_ == 0) { + return ExecNonNull(); + } + + // Bit counters used for both null_selection behaviors + DropNullCounter drop_null_counter(filter_is_valid_, filter_data_, filter_offset_, + values_length_); + OptionalBitBlockCounter data_counter(values_is_valid_, values_offset_, + values_length_); + OptionalBitBlockCounter filter_valid_counter(filter_is_valid_, filter_offset_, + values_length_); + + auto WriteNotNull = [&](int64_t index) { + BitUtil::SetBit(out_is_valid_, out_offset_ + out_position_); + // Increments out_position_ + WriteValue(index); + }; + + auto WriteMaybeNull = [&](int64_t index) { + BitUtil::SetBitTo(out_is_valid_, out_offset_ + out_position_, + BitUtil::GetBit(values_is_valid_, values_offset_ + index)); + // Increments out_position_ + WriteValue(index); + }; + + int64_t in_position = 0; + while (in_position < values_length_) { + BitBlockCount filter_block = drop_null_counter.NextBlock(); + BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); + BitBlockCount data_block = data_counter.NextWord(); + if (filter_block.AllSet() && data_block.AllSet()) { + // Fastest path: all values in block are included and not null + BitUtil::SetBitsTo(out_is_valid_, out_offset_ + out_position_, + filter_block.length, true); + WriteValueSegment(in_position, filter_block.length); + in_position += filter_block.length; + } else if (filter_block.AllSet()) { + // Faster: all values are selected, but some values are null + // Batch copy bits from values validity bitmap to output validity bitmap + CopyBitmap(values_is_valid_, values_offset_ + in_position, filter_block.length, + out_is_valid_, out_offset_ + out_position_); + WriteValueSegment(in_position, filter_block.length); + in_position += filter_block.length; + } else if (filter_block.NoneSet() && null_selection_ == FilterOptions::DROP) { + // For this exceedingly common case in low-selectivity filters we can + // skip further analysis of the data and move on to the next block. + in_position += filter_block.length; + } else { + // Some filter values are false or null + if (data_block.AllSet()) { + // No values are null + if (filter_valid_block.AllSet()) { + // Filter is non-null but some values are false + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + WriteNotNull(in_position); + } + ++in_position; + } + } else if (null_selection_ == FilterOptions::DROP) { + // If any values are selected, they ARE NOT null + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position) && + BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + WriteNotNull(in_position); + } + ++in_position; + } + } else { // null_selection == FilterOptions::EMIT_NULL + // Data values in this block are not null + for (int64_t i = 0; i < filter_block.length; ++i) { + const bool is_valid = + BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position); + if (is_valid && + BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + // Filter slot is non-null and set + WriteNotNull(in_position); + } else if (!is_valid) { + // Filter slot is null, so we have a null in the output + BitUtil::ClearBit(out_is_valid_, out_offset_ + out_position_); + WriteNull(); + } + ++in_position; + } + } + } else { // !data_block.AllSet() + // Some values are null + if (filter_valid_block.AllSet()) { + // Filter is non-null but some values are false + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + WriteMaybeNull(in_position); + } + ++in_position; + } + } else if (null_selection_ == FilterOptions::DROP) { + // If any values are selected, they ARE NOT null + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position) && + BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + WriteMaybeNull(in_position); + } + ++in_position; + } + } else { // null_selection == FilterOptions::EMIT_NULL + // Data values in this block are not null + for (int64_t i = 0; i < filter_block.length; ++i) { + const bool is_valid = + BitUtil::GetBit(filter_is_valid_, filter_offset_ + in_position); + if (is_valid && + BitUtil::GetBit(filter_data_, filter_offset_ + in_position)) { + // Filter slot is non-null and set + WriteMaybeNull(in_position); + } else if (!is_valid) { + // Filter slot is null, so we have a null in the output + BitUtil::ClearBit(out_is_valid_, out_offset_ + out_position_); + WriteNull(); + } + ++in_position; + } + } + } + } // !filter_block.AllSet() + } // while(in_position < values_length_) + } + + // Write the next out_position given the selected in_position for the input + // data and advance out_position + void WriteValue(int64_t in_position) { + out_data_[out_position_++] = values_data_[in_position]; + } + + void WriteValueSegment(int64_t in_start, int64_t length) { + std::memcpy(out_data_ + out_position_, values_data_ + in_start, length * sizeof(T)); + out_position_ += length; + } + + void WriteNull() { + // Zero the memory + out_data_[out_position_++] = T{}; + } + + private: + const uint8_t* values_is_valid_; + const T* values_data_; + int64_t values_null_count_; + int64_t values_offset_; + int64_t values_length_; + const uint8_t* filter_is_valid_; + const uint8_t* filter_data_; + int64_t filter_null_count_; + int64_t filter_offset_; + FilterOptions::NullSelectionBehavior null_selection_; + uint8_t* out_is_valid_; + T* out_data_; + int64_t out_offset_; + int64_t out_length_; + int64_t out_position_; +}; + +template <> +inline void PrimitiveFilterImpl<BooleanType>::WriteValue(int64_t in_position) { + BitUtil::SetBitTo(out_data_, out_offset_ + out_position_++, + BitUtil::GetBit(values_data_, values_offset_ + in_position)); +} + +template <> +inline void PrimitiveFilterImpl<BooleanType>::WriteValueSegment(int64_t in_start, + int64_t length) { + CopyBitmap(values_data_, values_offset_ + in_start, length, out_data_, + out_offset_ + out_position_); + out_position_ += length; +} + +template <> +inline void PrimitiveFilterImpl<BooleanType>::WriteNull() { + // Zero the bit + BitUtil::ClearBit(out_data_, out_offset_ + out_position_++); +} + Status PrimitiveFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - PrimitiveArg values = GetPrimitiveArg(*batch[0].array()); - PrimitiveArg filter = GetPrimitiveArg(*batch[1].array()); - FilterOptions::NullSelectionBehavior null_selection = - FilterState::Get(ctx).null_selection_behavior; - - int64_t output_length = GetFilterOutputSize(*batch[1].array(), null_selection); - - ArrayData* out_arr = out->mutable_array(); - - // The output precomputed null count is unknown except in the narrow - // condition that all the values are non-null and the filter will not cause - // any new nulls to be created. - if (values.null_count == 0 && - (null_selection == FilterOptions::DROP || filter.null_count == 0)) { - out_arr->null_count = 0; - } else { - out_arr->null_count = kUnknownNullCount; - } - - // When neither the values nor filter is known to have any nulls, we will - // elect the optimized ExecNonNull path where there is no need to populate a - // validity bitmap. - bool allocate_validity = values.null_count != 0 || filter.null_count != 0; - + PrimitiveArg values = GetPrimitiveArg(*batch[0].array()); + PrimitiveArg filter = GetPrimitiveArg(*batch[1].array()); + FilterOptions::NullSelectionBehavior null_selection = + FilterState::Get(ctx).null_selection_behavior; + + int64_t output_length = GetFilterOutputSize(*batch[1].array(), null_selection); + + ArrayData* out_arr = out->mutable_array(); + + // The output precomputed null count is unknown except in the narrow + // condition that all the values are non-null and the filter will not cause + // any new nulls to be created. + if (values.null_count == 0 && + (null_selection == FilterOptions::DROP || filter.null_count == 0)) { + out_arr->null_count = 0; + } else { + out_arr->null_count = kUnknownNullCount; + } + + // When neither the values nor filter is known to have any nulls, we will + // elect the optimized ExecNonNull path where there is no need to populate a + // validity bitmap. + bool allocate_validity = values.null_count != 0 || filter.null_count != 0; + RETURN_NOT_OK( PreallocateData(ctx, output_length, values.bit_width, allocate_validity, out_arr)); - - switch (values.bit_width) { - case 1: + + switch (values.bit_width) { + case 1: PrimitiveFilterImpl<BooleanType>(values, filter, null_selection, out_arr).Exec(); break; - case 8: + case 8: PrimitiveFilterImpl<UInt8Type>(values, filter, null_selection, out_arr).Exec(); break; - case 16: + case 16: PrimitiveFilterImpl<UInt16Type>(values, filter, null_selection, out_arr).Exec(); break; - case 32: + case 32: PrimitiveFilterImpl<UInt32Type>(values, filter, null_selection, out_arr).Exec(); break; - case 64: + case 64: PrimitiveFilterImpl<UInt64Type>(values, filter, null_selection, out_arr).Exec(); break; - default: - DCHECK(false) << "Invalid values bit width"; - break; - } + default: + DCHECK(false) << "Invalid values bit width"; + break; + } return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Optimized filter for base binary types (32-bit and 64-bit) - -#define BINARY_FILTER_SETUP_COMMON() \ - auto raw_offsets = \ - reinterpret_cast<const offset_type*>(values.buffers[1]->data()) + values.offset; \ - const uint8_t* raw_data = values.buffers[2]->data(); \ - \ - TypedBufferBuilder<offset_type> offset_builder(ctx->memory_pool()); \ - TypedBufferBuilder<uint8_t> data_builder(ctx->memory_pool()); \ - RETURN_NOT_OK(offset_builder.Reserve(output_length + 1)); \ - \ - /* Presize the data builder with a rough estimate */ \ - if (values.length > 0) { \ - const double mean_value_length = (raw_offsets[values.length] - raw_offsets[0]) / \ - static_cast<double>(values.length); \ - RETURN_NOT_OK( \ - data_builder.Reserve(static_cast<int64_t>(mean_value_length * output_length))); \ - } \ - int64_t space_available = data_builder.capacity(); \ - offset_type offset = 0; - -#define APPEND_RAW_DATA(DATA, NBYTES) \ - if (ARROW_PREDICT_FALSE(NBYTES > space_available)) { \ - RETURN_NOT_OK(data_builder.Reserve(NBYTES)); \ - space_available = data_builder.capacity() - data_builder.length(); \ - } \ - data_builder.UnsafeAppend(DATA, NBYTES); \ - space_available -= NBYTES - -#define APPEND_SINGLE_VALUE() \ - do { \ - offset_type val_size = raw_offsets[in_position + 1] - raw_offsets[in_position]; \ - APPEND_RAW_DATA(raw_data + raw_offsets[in_position], val_size); \ - offset += val_size; \ - } while (0) - -// Optimized binary filter for the case where neither values nor filter have -// nulls -template <typename Type> -Status BinaryFilterNonNullImpl(KernelContext* ctx, const ArrayData& values, - const ArrayData& filter, int64_t output_length, - FilterOptions::NullSelectionBehavior null_selection, - ArrayData* out) { - using offset_type = typename Type::offset_type; - const auto filter_data = filter.buffers[1]->data(); - - BINARY_FILTER_SETUP_COMMON(); - +} + +// ---------------------------------------------------------------------- +// Optimized filter for base binary types (32-bit and 64-bit) + +#define BINARY_FILTER_SETUP_COMMON() \ + auto raw_offsets = \ + reinterpret_cast<const offset_type*>(values.buffers[1]->data()) + values.offset; \ + const uint8_t* raw_data = values.buffers[2]->data(); \ + \ + TypedBufferBuilder<offset_type> offset_builder(ctx->memory_pool()); \ + TypedBufferBuilder<uint8_t> data_builder(ctx->memory_pool()); \ + RETURN_NOT_OK(offset_builder.Reserve(output_length + 1)); \ + \ + /* Presize the data builder with a rough estimate */ \ + if (values.length > 0) { \ + const double mean_value_length = (raw_offsets[values.length] - raw_offsets[0]) / \ + static_cast<double>(values.length); \ + RETURN_NOT_OK( \ + data_builder.Reserve(static_cast<int64_t>(mean_value_length * output_length))); \ + } \ + int64_t space_available = data_builder.capacity(); \ + offset_type offset = 0; + +#define APPEND_RAW_DATA(DATA, NBYTES) \ + if (ARROW_PREDICT_FALSE(NBYTES > space_available)) { \ + RETURN_NOT_OK(data_builder.Reserve(NBYTES)); \ + space_available = data_builder.capacity() - data_builder.length(); \ + } \ + data_builder.UnsafeAppend(DATA, NBYTES); \ + space_available -= NBYTES + +#define APPEND_SINGLE_VALUE() \ + do { \ + offset_type val_size = raw_offsets[in_position + 1] - raw_offsets[in_position]; \ + APPEND_RAW_DATA(raw_data + raw_offsets[in_position], val_size); \ + offset += val_size; \ + } while (0) + +// Optimized binary filter for the case where neither values nor filter have +// nulls +template <typename Type> +Status BinaryFilterNonNullImpl(KernelContext* ctx, const ArrayData& values, + const ArrayData& filter, int64_t output_length, + FilterOptions::NullSelectionBehavior null_selection, + ArrayData* out) { + using offset_type = typename Type::offset_type; + const auto filter_data = filter.buffers[1]->data(); + + BINARY_FILTER_SETUP_COMMON(); + RETURN_NOT_OK(arrow::internal::VisitSetBitRuns( filter_data, filter.offset, filter.length, [&](int64_t position, int64_t length) { - // Bulk-append raw data + // Bulk-append raw data const offset_type run_data_bytes = (raw_offsets[position + length] - raw_offsets[position]); APPEND_RAW_DATA(raw_data + raw_offsets[position], run_data_bytes); - // Append offsets + // Append offsets offset_type cur_offset = raw_offsets[position]; for (int64_t i = 0; i < length; ++i) { - offset_builder.UnsafeAppend(offset); + offset_builder.UnsafeAppend(offset); offset += raw_offsets[i + position + 1] - cur_offset; cur_offset = raw_offsets[i + position + 1]; - } + } return Status::OK(); })); - offset_builder.UnsafeAppend(offset); - out->length = output_length; - RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); - return data_builder.Finish(&out->buffers[2]); -} - -template <typename Type> -Status BinaryFilterImpl(KernelContext* ctx, const ArrayData& values, - const ArrayData& filter, int64_t output_length, - FilterOptions::NullSelectionBehavior null_selection, - ArrayData* out) { - using offset_type = typename Type::offset_type; - - const auto filter_data = filter.buffers[1]->data(); - const uint8_t* filter_is_valid = GetValidityBitmap(filter); - const int64_t filter_offset = filter.offset; - - const uint8_t* values_is_valid = GetValidityBitmap(values); - const int64_t values_offset = values.offset; - - uint8_t* out_is_valid = out->buffers[0]->mutable_data(); - // Zero bits and then only have to set valid values to true - BitUtil::SetBitsTo(out_is_valid, 0, output_length, false); - - // We use 3 block counters for fast scanning of the filter - // - // * values_valid_counter: for values null/not-null - // * filter_valid_counter: for filter null/not-null - // * filter_counter: for filter true/false - OptionalBitBlockCounter values_valid_counter(values_is_valid, values_offset, - values.length); - OptionalBitBlockCounter filter_valid_counter(filter_is_valid, filter_offset, - filter.length); - BitBlockCounter filter_counter(filter_data, filter_offset, filter.length); - - BINARY_FILTER_SETUP_COMMON(); - + offset_builder.UnsafeAppend(offset); + out->length = output_length; + RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); + return data_builder.Finish(&out->buffers[2]); +} + +template <typename Type> +Status BinaryFilterImpl(KernelContext* ctx, const ArrayData& values, + const ArrayData& filter, int64_t output_length, + FilterOptions::NullSelectionBehavior null_selection, + ArrayData* out) { + using offset_type = typename Type::offset_type; + + const auto filter_data = filter.buffers[1]->data(); + const uint8_t* filter_is_valid = GetValidityBitmap(filter); + const int64_t filter_offset = filter.offset; + + const uint8_t* values_is_valid = GetValidityBitmap(values); + const int64_t values_offset = values.offset; + + uint8_t* out_is_valid = out->buffers[0]->mutable_data(); + // Zero bits and then only have to set valid values to true + BitUtil::SetBitsTo(out_is_valid, 0, output_length, false); + + // We use 3 block counters for fast scanning of the filter + // + // * values_valid_counter: for values null/not-null + // * filter_valid_counter: for filter null/not-null + // * filter_counter: for filter true/false + OptionalBitBlockCounter values_valid_counter(values_is_valid, values_offset, + values.length); + OptionalBitBlockCounter filter_valid_counter(filter_is_valid, filter_offset, + filter.length); + BitBlockCounter filter_counter(filter_data, filter_offset, filter.length); + + BINARY_FILTER_SETUP_COMMON(); + int64_t in_position = 0; int64_t out_position = 0; - while (in_position < filter.length) { - BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); - BitBlockCount values_valid_block = values_valid_counter.NextWord(); - BitBlockCount filter_block = filter_counter.NextWord(); - if (filter_block.NoneSet() && null_selection == FilterOptions::DROP) { - // For this exceedingly common case in low-selectivity filters we can - // skip further analysis of the data and move on to the next block. - in_position += filter_block.length; - } else if (filter_valid_block.AllSet()) { - // Simpler path: no filter values are null - if (filter_block.AllSet()) { - // Fastest path: filter values are all true and not null - if (values_valid_block.AllSet()) { - // The values aren't null either - BitUtil::SetBitsTo(out_is_valid, out_position, filter_block.length, true); - - // Bulk-append raw data - offset_type block_data_bytes = - (raw_offsets[in_position + filter_block.length] - raw_offsets[in_position]); - APPEND_RAW_DATA(raw_data + raw_offsets[in_position], block_data_bytes); - // Append offsets - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - offset_builder.UnsafeAppend(offset); - offset += raw_offsets[in_position + 1] - raw_offsets[in_position]; - } - out_position += filter_block.length; - } else { - // Some of the values in this block are null - for (int64_t i = 0; i < filter_block.length; - ++i, ++in_position, ++out_position) { - offset_builder.UnsafeAppend(offset); - if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { - BitUtil::SetBit(out_is_valid, out_position); - APPEND_SINGLE_VALUE(); - } - } - } - } else { // !filter_block.AllSet() - // Some of the filter values are false, but all not null - if (values_valid_block.AllSet()) { - // All the values are not-null, so we can skip null checking for - // them - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - BitUtil::SetBit(out_is_valid, out_position++); - APPEND_SINGLE_VALUE(); - } - } - } else { - // Some of the values in the block are null, so we have to check - // each one - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { - BitUtil::SetBit(out_is_valid, out_position); - APPEND_SINGLE_VALUE(); - } - ++out_position; - } - } - } - } - } else { // !filter_valid_block.AllSet() - // Some of the filter values are null, so we have to handle the DROP - // versus EMIT_NULL null selection behavior. - if (null_selection == FilterOptions::DROP) { - // Filter null values are treated as false. - if (values_valid_block.AllSet()) { - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - BitUtil::SetBit(out_is_valid, out_position++); - APPEND_SINGLE_VALUE(); - } - } - } else { - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { - BitUtil::SetBit(out_is_valid, out_position); - APPEND_SINGLE_VALUE(); - } - ++out_position; - } - } - } - } else { - // EMIT_NULL - - // Filter null values are appended to output as null whether the - // value in the corresponding slot is valid or not - if (values_valid_block.AllSet()) { - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - const bool filter_not_null = - BitUtil::GetBit(filter_is_valid, filter_offset + in_position); - if (filter_not_null && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - BitUtil::SetBit(out_is_valid, out_position++); - APPEND_SINGLE_VALUE(); - } else if (!filter_not_null) { - offset_builder.UnsafeAppend(offset); - ++out_position; - } - } - } else { - for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { - const bool filter_not_null = - BitUtil::GetBit(filter_is_valid, filter_offset + in_position); - if (filter_not_null && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - offset_builder.UnsafeAppend(offset); - if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { - BitUtil::SetBit(out_is_valid, out_position); - APPEND_SINGLE_VALUE(); - } - ++out_position; - } else if (!filter_not_null) { - offset_builder.UnsafeAppend(offset); - ++out_position; - } - } - } - } - } - } - offset_builder.UnsafeAppend(offset); - out->length = output_length; - RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); - return data_builder.Finish(&out->buffers[2]); -} - -#undef BINARY_FILTER_SETUP_COMMON -#undef APPEND_RAW_DATA -#undef APPEND_SINGLE_VALUE - + while (in_position < filter.length) { + BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); + BitBlockCount values_valid_block = values_valid_counter.NextWord(); + BitBlockCount filter_block = filter_counter.NextWord(); + if (filter_block.NoneSet() && null_selection == FilterOptions::DROP) { + // For this exceedingly common case in low-selectivity filters we can + // skip further analysis of the data and move on to the next block. + in_position += filter_block.length; + } else if (filter_valid_block.AllSet()) { + // Simpler path: no filter values are null + if (filter_block.AllSet()) { + // Fastest path: filter values are all true and not null + if (values_valid_block.AllSet()) { + // The values aren't null either + BitUtil::SetBitsTo(out_is_valid, out_position, filter_block.length, true); + + // Bulk-append raw data + offset_type block_data_bytes = + (raw_offsets[in_position + filter_block.length] - raw_offsets[in_position]); + APPEND_RAW_DATA(raw_data + raw_offsets[in_position], block_data_bytes); + // Append offsets + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + offset_builder.UnsafeAppend(offset); + offset += raw_offsets[in_position + 1] - raw_offsets[in_position]; + } + out_position += filter_block.length; + } else { + // Some of the values in this block are null + for (int64_t i = 0; i < filter_block.length; + ++i, ++in_position, ++out_position) { + offset_builder.UnsafeAppend(offset); + if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { + BitUtil::SetBit(out_is_valid, out_position); + APPEND_SINGLE_VALUE(); + } + } + } + } else { // !filter_block.AllSet() + // Some of the filter values are false, but all not null + if (values_valid_block.AllSet()) { + // All the values are not-null, so we can skip null checking for + // them + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + BitUtil::SetBit(out_is_valid, out_position++); + APPEND_SINGLE_VALUE(); + } + } + } else { + // Some of the values in the block are null, so we have to check + // each one + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { + BitUtil::SetBit(out_is_valid, out_position); + APPEND_SINGLE_VALUE(); + } + ++out_position; + } + } + } + } + } else { // !filter_valid_block.AllSet() + // Some of the filter values are null, so we have to handle the DROP + // versus EMIT_NULL null selection behavior. + if (null_selection == FilterOptions::DROP) { + // Filter null values are treated as false. + if (values_valid_block.AllSet()) { + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + BitUtil::SetBit(out_is_valid, out_position++); + APPEND_SINGLE_VALUE(); + } + } + } else { + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { + BitUtil::SetBit(out_is_valid, out_position); + APPEND_SINGLE_VALUE(); + } + ++out_position; + } + } + } + } else { + // EMIT_NULL + + // Filter null values are appended to output as null whether the + // value in the corresponding slot is valid or not + if (values_valid_block.AllSet()) { + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + const bool filter_not_null = + BitUtil::GetBit(filter_is_valid, filter_offset + in_position); + if (filter_not_null && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + BitUtil::SetBit(out_is_valid, out_position++); + APPEND_SINGLE_VALUE(); + } else if (!filter_not_null) { + offset_builder.UnsafeAppend(offset); + ++out_position; + } + } + } else { + for (int64_t i = 0; i < filter_block.length; ++i, ++in_position) { + const bool filter_not_null = + BitUtil::GetBit(filter_is_valid, filter_offset + in_position); + if (filter_not_null && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + offset_builder.UnsafeAppend(offset); + if (BitUtil::GetBit(values_is_valid, values_offset + in_position)) { + BitUtil::SetBit(out_is_valid, out_position); + APPEND_SINGLE_VALUE(); + } + ++out_position; + } else if (!filter_not_null) { + offset_builder.UnsafeAppend(offset); + ++out_position; + } + } + } + } + } + } + offset_builder.UnsafeAppend(offset); + out->length = output_length; + RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); + return data_builder.Finish(&out->buffers[2]); +} + +#undef BINARY_FILTER_SETUP_COMMON +#undef APPEND_RAW_DATA +#undef APPEND_SINGLE_VALUE + Status BinaryFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - FilterOptions::NullSelectionBehavior null_selection = - FilterState::Get(ctx).null_selection_behavior; - - const ArrayData& values = *batch[0].array(); - const ArrayData& filter = *batch[1].array(); - int64_t output_length = GetFilterOutputSize(filter, null_selection); - ArrayData* out_arr = out->mutable_array(); - - // The output precomputed null count is unknown except in the narrow - // condition that all the values are non-null and the filter will not cause - // any new nulls to be created. - if (values.null_count == 0 && - (null_selection == FilterOptions::DROP || filter.null_count == 0)) { - out_arr->null_count = 0; - } else { - out_arr->null_count = kUnknownNullCount; - } - Type::type type_id = values.type->id(); - if (values.null_count == 0 && filter.null_count == 0) { - // Faster no-nulls case - if (is_binary_like(type_id)) { + FilterOptions::NullSelectionBehavior null_selection = + FilterState::Get(ctx).null_selection_behavior; + + const ArrayData& values = *batch[0].array(); + const ArrayData& filter = *batch[1].array(); + int64_t output_length = GetFilterOutputSize(filter, null_selection); + ArrayData* out_arr = out->mutable_array(); + + // The output precomputed null count is unknown except in the narrow + // condition that all the values are non-null and the filter will not cause + // any new nulls to be created. + if (values.null_count == 0 && + (null_selection == FilterOptions::DROP || filter.null_count == 0)) { + out_arr->null_count = 0; + } else { + out_arr->null_count = kUnknownNullCount; + } + Type::type type_id = values.type->id(); + if (values.null_count == 0 && filter.null_count == 0) { + // Faster no-nulls case + if (is_binary_like(type_id)) { RETURN_NOT_OK(BinaryFilterNonNullImpl<BinaryType>( ctx, values, filter, output_length, null_selection, out_arr)); - } else if (is_large_binary_like(type_id)) { + } else if (is_large_binary_like(type_id)) { RETURN_NOT_OK(BinaryFilterNonNullImpl<LargeBinaryType>( ctx, values, filter, output_length, null_selection, out_arr)); - } else { - DCHECK(false); - } - } else { - // Output may have nulls + } else { + DCHECK(false); + } + } else { + // Output may have nulls RETURN_NOT_OK(ctx->AllocateBitmap(output_length).Value(&out_arr->buffers[0])); - if (is_binary_like(type_id)) { + if (is_binary_like(type_id)) { RETURN_NOT_OK(BinaryFilterImpl<BinaryType>(ctx, values, filter, output_length, null_selection, out_arr)); - } else if (is_large_binary_like(type_id)) { + } else if (is_large_binary_like(type_id)) { RETURN_NOT_OK(BinaryFilterImpl<LargeBinaryType>(ctx, values, filter, output_length, null_selection, out_arr)); - } else { - DCHECK(false); - } - } + } else { + DCHECK(false); + } + } return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Null take and filter - +} + +// ---------------------------------------------------------------------- +// Null take and filter + Status NullTake(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (TakeState::Get(ctx).boundscheck) { + if (TakeState::Get(ctx).boundscheck) { RETURN_NOT_OK(CheckIndexBounds(*batch[1].array(), batch[0].length())); - } - // batch.length doesn't take into account the take indices - auto new_length = batch[1].array()->length; - out->value = std::make_shared<NullArray>(new_length)->data(); + } + // batch.length doesn't take into account the take indices + auto new_length = batch[1].array()->length; + out->value = std::make_shared<NullArray>(new_length)->data(); return Status::OK(); -} - +} + Status NullFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - int64_t output_length = GetFilterOutputSize( - *batch[1].array(), FilterState::Get(ctx).null_selection_behavior); - out->value = std::make_shared<NullArray>(output_length)->data(); + int64_t output_length = GetFilterOutputSize( + *batch[1].array(), FilterState::Get(ctx).null_selection_behavior); + out->value = std::make_shared<NullArray>(output_length)->data(); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Dictionary take and filter - +} + +// ---------------------------------------------------------------------- +// Dictionary take and filter + Status DictionaryTake(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DictionaryArray values(batch[0].array()); - Datum result; + DictionaryArray values(batch[0].array()); + Datum result; RETURN_NOT_OK( Take(Datum(values.indices()), batch[1], TakeState::Get(ctx), ctx->exec_context()) .Value(&result)); - DictionaryArray taken_values(values.type(), result.make_array(), values.dictionary()); - out->value = taken_values.data(); + DictionaryArray taken_values(values.type(), result.make_array(), values.dictionary()); + out->value = taken_values.data(); return Status::OK(); -} - +} + Status DictionaryFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - DictionaryArray dict_values(batch[0].array()); - Datum result; + DictionaryArray dict_values(batch[0].array()); + Datum result; RETURN_NOT_OK(Filter(Datum(dict_values.indices()), batch[1].array(), FilterState::Get(ctx), ctx->exec_context()) .Value(&result)); - DictionaryArray filtered_values(dict_values.type(), result.make_array(), - dict_values.dictionary()); - out->value = filtered_values.data(); + DictionaryArray filtered_values(dict_values.type(), result.make_array(), + dict_values.dictionary()); + out->value = filtered_values.data(); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Extension take and filter - +} + +// ---------------------------------------------------------------------- +// Extension take and filter + Status ExtensionTake(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - ExtensionArray values(batch[0].array()); - Datum result; + ExtensionArray values(batch[0].array()); + Datum result; RETURN_NOT_OK( Take(Datum(values.storage()), batch[1], TakeState::Get(ctx), ctx->exec_context()) .Value(&result)); - ExtensionArray taken_values(values.type(), result.make_array()); - out->value = taken_values.data(); + ExtensionArray taken_values(values.type(), result.make_array()); + out->value = taken_values.data(); return Status::OK(); -} - +} + Status ExtensionFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - ExtensionArray ext_values(batch[0].array()); - Datum result; + ExtensionArray ext_values(batch[0].array()); + Datum result; RETURN_NOT_OK(Filter(Datum(ext_values.storage()), batch[1].array(), FilterState::Get(ctx), ctx->exec_context()) .Value(&result)); - ExtensionArray filtered_values(ext_values.type(), result.make_array()); - out->value = filtered_values.data(); + ExtensionArray filtered_values(ext_values.type(), result.make_array()); + out->value = filtered_values.data(); return Status::OK(); -} - -// ---------------------------------------------------------------------- -// Implement take for other data types where there is less performance -// sensitivity by visiting the selected indices. - -// Use CRTP to dispatch to type-specific processing of take indices for each -// unsigned integer type. -template <typename Impl, typename Type> -struct Selection { - using ValuesArrayType = typename TypeTraits<Type>::ArrayType; - - // Forwards the generic value visitors to the take index visitor template - template <typename IndexCType> - struct TakeAdapter { - static constexpr bool is_take = true; - - Impl* impl; - explicit TakeAdapter(Impl* impl) : impl(impl) {} - template <typename ValidVisitor, typename NullVisitor> - Status Generate(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { - return impl->template VisitTake<IndexCType>(std::forward<ValidVisitor>(visit_valid), - std::forward<NullVisitor>(visit_null)); - } - }; - - // Forwards the generic value visitors to the VisitFilter template - struct FilterAdapter { - static constexpr bool is_take = false; - - Impl* impl; - explicit FilterAdapter(Impl* impl) : impl(impl) {} - template <typename ValidVisitor, typename NullVisitor> - Status Generate(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { - return impl->VisitFilter(std::forward<ValidVisitor>(visit_valid), - std::forward<NullVisitor>(visit_null)); - } - }; - - KernelContext* ctx; - std::shared_ptr<ArrayData> values; - std::shared_ptr<ArrayData> selection; - int64_t output_length; - ArrayData* out; - TypedBufferBuilder<bool> validity_builder; - - Selection(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) - : ctx(ctx), - values(batch[0].array()), - selection(batch[1].array()), - output_length(output_length), - out(out->mutable_array()), - validity_builder(ctx->memory_pool()) {} - - virtual ~Selection() = default; - - Status FinishCommon() { - out->buffers.resize(values->buffers.size()); - out->length = validity_builder.length(); - out->null_count = validity_builder.false_count(); - return validity_builder.Finish(&out->buffers[0]); - } - - template <typename IndexCType, typename ValidVisitor, typename NullVisitor> - Status VisitTake(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { - const auto indices_values = selection->GetValues<IndexCType>(1); - const uint8_t* is_valid = GetValidityBitmap(*selection); - OptionalBitIndexer indices_is_valid(selection->buffers[0], selection->offset); - OptionalBitIndexer values_is_valid(values->buffers[0], values->offset); - - const bool values_have_nulls = values->MayHaveNulls(); - OptionalBitBlockCounter bit_counter(is_valid, selection->offset, selection->length); - int64_t position = 0; - while (position < selection->length) { - BitBlockCount block = bit_counter.NextBlock(); - const bool indices_have_nulls = block.popcount < block.length; - if (!indices_have_nulls && !values_have_nulls) { - // Fastest path, neither indices nor values have nulls - validity_builder.UnsafeAppend(block.length, true); - for (int64_t i = 0; i < block.length; ++i) { - RETURN_NOT_OK(visit_valid(indices_values[position++])); - } - } else if (block.popcount > 0) { - // Since we have to branch on whether the indices are null or not, we - // combine the "non-null indices block but some values null" and - // "some-null indices block but values non-null" into a single loop. - for (int64_t i = 0; i < block.length; ++i) { - if ((!indices_have_nulls || indices_is_valid[position]) && - values_is_valid[indices_values[position]]) { - validity_builder.UnsafeAppend(true); - RETURN_NOT_OK(visit_valid(indices_values[position])); - } else { - validity_builder.UnsafeAppend(false); - RETURN_NOT_OK(visit_null()); - } - ++position; - } - } else { - // The whole block is null - validity_builder.UnsafeAppend(block.length, false); - for (int64_t i = 0; i < block.length; ++i) { - RETURN_NOT_OK(visit_null()); - } - position += block.length; - } - } - return Status::OK(); - } - - // We use the NullVisitor both for "selected" nulls as well as "emitted" - // nulls coming from the filter when using FilterOptions::EMIT_NULL - template <typename ValidVisitor, typename NullVisitor> - Status VisitFilter(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { - auto null_selection = FilterState::Get(ctx).null_selection_behavior; - - const auto filter_data = selection->buffers[1]->data(); - - const uint8_t* filter_is_valid = GetValidityBitmap(*selection); - const int64_t filter_offset = selection->offset; - OptionalBitIndexer values_is_valid(values->buffers[0], values->offset); - - // We use 3 block counters for fast scanning of the filter - // - // * values_valid_counter: for values null/not-null - // * filter_valid_counter: for filter null/not-null - // * filter_counter: for filter true/false - OptionalBitBlockCounter values_valid_counter(GetValidityBitmap(*values), - values->offset, values->length); - OptionalBitBlockCounter filter_valid_counter(filter_is_valid, filter_offset, - selection->length); - BitBlockCounter filter_counter(filter_data, filter_offset, selection->length); - int64_t in_position = 0; - - auto AppendNotNull = [&](int64_t index) -> Status { - validity_builder.UnsafeAppend(true); - return visit_valid(index); - }; - - auto AppendNull = [&]() -> Status { - validity_builder.UnsafeAppend(false); - return visit_null(); - }; - - auto AppendMaybeNull = [&](int64_t index) -> Status { - if (values_is_valid[index]) { - return AppendNotNull(index); - } else { - return AppendNull(); - } - }; - - while (in_position < selection->length) { - BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); - BitBlockCount values_valid_block = values_valid_counter.NextWord(); - BitBlockCount filter_block = filter_counter.NextWord(); - if (filter_block.NoneSet() && null_selection == FilterOptions::DROP) { - // For this exceedingly common case in low-selectivity filters we can - // skip further analysis of the data and move on to the next block. - in_position += filter_block.length; - } else if (filter_valid_block.AllSet()) { - // Simpler path: no filter values are null - if (filter_block.AllSet()) { - // Fastest path: filter values are all true and not null - if (values_valid_block.AllSet()) { - // The values aren't null either - validity_builder.UnsafeAppend(filter_block.length, true); - for (int64_t i = 0; i < filter_block.length; ++i) { - RETURN_NOT_OK(visit_valid(in_position++)); - } - } else { - // Some of the values in this block are null - for (int64_t i = 0; i < filter_block.length; ++i) { - RETURN_NOT_OK(AppendMaybeNull(in_position++)); - } - } - } else { // !filter_block.AllSet() - // Some of the filter values are false, but all not null - if (values_valid_block.AllSet()) { - // All the values are not-null, so we can skip null checking for - // them - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { - RETURN_NOT_OK(AppendNotNull(in_position)); - } - ++in_position; - } - } else { - // Some of the values in the block are null, so we have to check - // each one - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { - RETURN_NOT_OK(AppendMaybeNull(in_position)); - } - ++in_position; - } - } - } - } else { // !filter_valid_block.AllSet() - // Some of the filter values are null, so we have to handle the DROP - // versus EMIT_NULL null selection behavior. - if (null_selection == FilterOptions::DROP) { - // Filter null values are treated as false. - for (int64_t i = 0; i < filter_block.length; ++i) { - if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - RETURN_NOT_OK(AppendMaybeNull(in_position)); - } - ++in_position; - } - } else { - // Filter null values are appended to output as null whether the - // value in the corresponding slot is valid or not - for (int64_t i = 0; i < filter_block.length; ++i) { - const bool filter_not_null = - BitUtil::GetBit(filter_is_valid, filter_offset + in_position); - if (filter_not_null && - BitUtil::GetBit(filter_data, filter_offset + in_position)) { - RETURN_NOT_OK(AppendMaybeNull(in_position)); - } else if (!filter_not_null) { - // EMIT_NULL case - RETURN_NOT_OK(AppendNull()); - } - ++in_position; - } - } - } - } - return Status::OK(); - } - - virtual Status Init() { return Status::OK(); } - - // Implementation specific finish logic - virtual Status Finish() = 0; - - Status ExecTake() { - RETURN_NOT_OK(this->validity_builder.Reserve(output_length)); - RETURN_NOT_OK(Init()); - int index_width = GetByteWidth(*this->selection->type); - - // CTRP dispatch here - switch (index_width) { - case 1: { - Status s = - static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint8_t>>(); - RETURN_NOT_OK(s); - } break; - case 2: { - Status s = - static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint16_t>>(); - RETURN_NOT_OK(s); - } break; - case 4: { - Status s = - static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint32_t>>(); - RETURN_NOT_OK(s); - } break; - case 8: { - Status s = - static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint64_t>>(); - RETURN_NOT_OK(s); - } break; - default: - DCHECK(false) << "Invalid index width"; - break; - } - RETURN_NOT_OK(this->FinishCommon()); - return Finish(); - } - - Status ExecFilter() { - RETURN_NOT_OK(this->validity_builder.Reserve(output_length)); - RETURN_NOT_OK(Init()); - // CRTP dispatch - Status s = static_cast<Impl*>(this)->template GenerateOutput<FilterAdapter>(); - RETURN_NOT_OK(s); - RETURN_NOT_OK(this->FinishCommon()); - return Finish(); - } -}; - -#define LIFT_BASE_MEMBERS() \ - using ValuesArrayType = typename Base::ValuesArrayType; \ - using Base::ctx; \ - using Base::values; \ - using Base::selection; \ - using Base::output_length; \ - using Base::out; \ - using Base::validity_builder - -static inline Status VisitNoop() { return Status::OK(); } - -// A selection implementation for 32-bit and 64-bit variable binary -// types. Common generated kernels are shared between Binary/String and -// LargeBinary/LargeString -template <typename Type> -struct VarBinaryImpl : public Selection<VarBinaryImpl<Type>, Type> { - using offset_type = typename Type::offset_type; - - using Base = Selection<VarBinaryImpl<Type>, Type>; - LIFT_BASE_MEMBERS(); - - std::shared_ptr<ArrayData> values_as_binary; - TypedBufferBuilder<offset_type> offset_builder; - TypedBufferBuilder<uint8_t> data_builder; - - static constexpr int64_t kOffsetLimit = std::numeric_limits<offset_type>::max() - 1; - - VarBinaryImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, - Datum* out) - : Base(ctx, batch, output_length, out), - offset_builder(ctx->memory_pool()), - data_builder(ctx->memory_pool()) {} - - template <typename Adapter> - Status GenerateOutput() { - ValuesArrayType typed_values(this->values_as_binary); - - // Presize the data builder with a rough estimate of the required data size - if (values->length > 0) { - const double mean_value_length = - (typed_values.total_values_length() / static_cast<double>(values->length)); - - // TODO: See if possible to reduce output_length for take/filter cases - // where there are nulls in the selection array - RETURN_NOT_OK( - data_builder.Reserve(static_cast<int64_t>(mean_value_length * output_length))); - } - int64_t space_available = data_builder.capacity(); - - const offset_type* raw_offsets = typed_values.raw_value_offsets(); - const uint8_t* raw_data = typed_values.raw_data(); - - offset_type offset = 0; - Adapter adapter(this); - RETURN_NOT_OK(adapter.Generate( - [&](int64_t index) { - offset_builder.UnsafeAppend(offset); - offset_type val_offset = raw_offsets[index]; - offset_type val_size = raw_offsets[index + 1] - val_offset; - - // Use static property to prune this code from the filter path in - // optimized builds - if (Adapter::is_take && - ARROW_PREDICT_FALSE(static_cast<int64_t>(offset) + - static_cast<int64_t>(val_size)) > kOffsetLimit) { - return Status::Invalid("Take operation overflowed binary array capacity"); - } - offset += val_size; - if (ARROW_PREDICT_FALSE(val_size > space_available)) { - RETURN_NOT_OK(data_builder.Reserve(val_size)); - space_available = data_builder.capacity() - data_builder.length(); - } - data_builder.UnsafeAppend(raw_data + val_offset, val_size); - space_available -= val_size; - return Status::OK(); - }, - [&]() { - offset_builder.UnsafeAppend(offset); - return Status::OK(); - })); - offset_builder.UnsafeAppend(offset); - return Status::OK(); - } - - Status Init() override { - ARROW_ASSIGN_OR_RAISE(this->values_as_binary, - GetArrayView(this->values, TypeTraits<Type>::type_singleton())); - return offset_builder.Reserve(output_length + 1); - } - - Status Finish() override { - RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); - return data_builder.Finish(&out->buffers[2]); - } -}; - -struct FSBImpl : public Selection<FSBImpl, FixedSizeBinaryType> { - using Base = Selection<FSBImpl, FixedSizeBinaryType>; - LIFT_BASE_MEMBERS(); - - TypedBufferBuilder<uint8_t> data_builder; - - FSBImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) - : Base(ctx, batch, output_length, out), data_builder(ctx->memory_pool()) {} - - template <typename Adapter> - Status GenerateOutput() { - FixedSizeBinaryArray typed_values(this->values); - int32_t value_size = typed_values.byte_width(); - - RETURN_NOT_OK(data_builder.Reserve(value_size * output_length)); - Adapter adapter(this); - return adapter.Generate( - [&](int64_t index) { - auto val = typed_values.GetView(index); - data_builder.UnsafeAppend(reinterpret_cast<const uint8_t*>(val.data()), - value_size); - return Status::OK(); - }, - [&]() { - data_builder.UnsafeAppend(value_size, static_cast<uint8_t>(0x00)); - return Status::OK(); - }); - } - - Status Finish() override { return data_builder.Finish(&out->buffers[1]); } -}; - -template <typename Type> -struct ListImpl : public Selection<ListImpl<Type>, Type> { - using offset_type = typename Type::offset_type; - - using Base = Selection<ListImpl<Type>, Type>; - LIFT_BASE_MEMBERS(); - - TypedBufferBuilder<offset_type> offset_builder; - typename TypeTraits<Type>::OffsetBuilderType child_index_builder; - - ListImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) - : Base(ctx, batch, output_length, out), - offset_builder(ctx->memory_pool()), - child_index_builder(ctx->memory_pool()) {} - - template <typename Adapter> - Status GenerateOutput() { - ValuesArrayType typed_values(this->values); - - // TODO presize child_index_builder with a similar heuristic as VarBinaryImpl - - offset_type offset = 0; - Adapter adapter(this); - RETURN_NOT_OK(adapter.Generate( - [&](int64_t index) { - offset_builder.UnsafeAppend(offset); - offset_type value_offset = typed_values.value_offset(index); - offset_type value_length = typed_values.value_length(index); - offset += value_length; - RETURN_NOT_OK(child_index_builder.Reserve(value_length)); - for (offset_type j = value_offset; j < value_offset + value_length; ++j) { - child_index_builder.UnsafeAppend(j); - } - return Status::OK(); - }, - [&]() { - offset_builder.UnsafeAppend(offset); - return Status::OK(); - })); - offset_builder.UnsafeAppend(offset); - return Status::OK(); - } - - Status Init() override { - RETURN_NOT_OK(offset_builder.Reserve(output_length + 1)); - return Status::OK(); - } - - Status Finish() override { - std::shared_ptr<Array> child_indices; - RETURN_NOT_OK(child_index_builder.Finish(&child_indices)); - - ValuesArrayType typed_values(this->values); - - // No need to boundscheck the child values indices - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Array> taken_child, - Take(*typed_values.values(), *child_indices, - TakeOptions::NoBoundsCheck(), ctx->exec_context())); - RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); - out->child_data = {taken_child->data()}; - return Status::OK(); - } -}; - +} + +// ---------------------------------------------------------------------- +// Implement take for other data types where there is less performance +// sensitivity by visiting the selected indices. + +// Use CRTP to dispatch to type-specific processing of take indices for each +// unsigned integer type. +template <typename Impl, typename Type> +struct Selection { + using ValuesArrayType = typename TypeTraits<Type>::ArrayType; + + // Forwards the generic value visitors to the take index visitor template + template <typename IndexCType> + struct TakeAdapter { + static constexpr bool is_take = true; + + Impl* impl; + explicit TakeAdapter(Impl* impl) : impl(impl) {} + template <typename ValidVisitor, typename NullVisitor> + Status Generate(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { + return impl->template VisitTake<IndexCType>(std::forward<ValidVisitor>(visit_valid), + std::forward<NullVisitor>(visit_null)); + } + }; + + // Forwards the generic value visitors to the VisitFilter template + struct FilterAdapter { + static constexpr bool is_take = false; + + Impl* impl; + explicit FilterAdapter(Impl* impl) : impl(impl) {} + template <typename ValidVisitor, typename NullVisitor> + Status Generate(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { + return impl->VisitFilter(std::forward<ValidVisitor>(visit_valid), + std::forward<NullVisitor>(visit_null)); + } + }; + + KernelContext* ctx; + std::shared_ptr<ArrayData> values; + std::shared_ptr<ArrayData> selection; + int64_t output_length; + ArrayData* out; + TypedBufferBuilder<bool> validity_builder; + + Selection(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) + : ctx(ctx), + values(batch[0].array()), + selection(batch[1].array()), + output_length(output_length), + out(out->mutable_array()), + validity_builder(ctx->memory_pool()) {} + + virtual ~Selection() = default; + + Status FinishCommon() { + out->buffers.resize(values->buffers.size()); + out->length = validity_builder.length(); + out->null_count = validity_builder.false_count(); + return validity_builder.Finish(&out->buffers[0]); + } + + template <typename IndexCType, typename ValidVisitor, typename NullVisitor> + Status VisitTake(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { + const auto indices_values = selection->GetValues<IndexCType>(1); + const uint8_t* is_valid = GetValidityBitmap(*selection); + OptionalBitIndexer indices_is_valid(selection->buffers[0], selection->offset); + OptionalBitIndexer values_is_valid(values->buffers[0], values->offset); + + const bool values_have_nulls = values->MayHaveNulls(); + OptionalBitBlockCounter bit_counter(is_valid, selection->offset, selection->length); + int64_t position = 0; + while (position < selection->length) { + BitBlockCount block = bit_counter.NextBlock(); + const bool indices_have_nulls = block.popcount < block.length; + if (!indices_have_nulls && !values_have_nulls) { + // Fastest path, neither indices nor values have nulls + validity_builder.UnsafeAppend(block.length, true); + for (int64_t i = 0; i < block.length; ++i) { + RETURN_NOT_OK(visit_valid(indices_values[position++])); + } + } else if (block.popcount > 0) { + // Since we have to branch on whether the indices are null or not, we + // combine the "non-null indices block but some values null" and + // "some-null indices block but values non-null" into a single loop. + for (int64_t i = 0; i < block.length; ++i) { + if ((!indices_have_nulls || indices_is_valid[position]) && + values_is_valid[indices_values[position]]) { + validity_builder.UnsafeAppend(true); + RETURN_NOT_OK(visit_valid(indices_values[position])); + } else { + validity_builder.UnsafeAppend(false); + RETURN_NOT_OK(visit_null()); + } + ++position; + } + } else { + // The whole block is null + validity_builder.UnsafeAppend(block.length, false); + for (int64_t i = 0; i < block.length; ++i) { + RETURN_NOT_OK(visit_null()); + } + position += block.length; + } + } + return Status::OK(); + } + + // We use the NullVisitor both for "selected" nulls as well as "emitted" + // nulls coming from the filter when using FilterOptions::EMIT_NULL + template <typename ValidVisitor, typename NullVisitor> + Status VisitFilter(ValidVisitor&& visit_valid, NullVisitor&& visit_null) { + auto null_selection = FilterState::Get(ctx).null_selection_behavior; + + const auto filter_data = selection->buffers[1]->data(); + + const uint8_t* filter_is_valid = GetValidityBitmap(*selection); + const int64_t filter_offset = selection->offset; + OptionalBitIndexer values_is_valid(values->buffers[0], values->offset); + + // We use 3 block counters for fast scanning of the filter + // + // * values_valid_counter: for values null/not-null + // * filter_valid_counter: for filter null/not-null + // * filter_counter: for filter true/false + OptionalBitBlockCounter values_valid_counter(GetValidityBitmap(*values), + values->offset, values->length); + OptionalBitBlockCounter filter_valid_counter(filter_is_valid, filter_offset, + selection->length); + BitBlockCounter filter_counter(filter_data, filter_offset, selection->length); + int64_t in_position = 0; + + auto AppendNotNull = [&](int64_t index) -> Status { + validity_builder.UnsafeAppend(true); + return visit_valid(index); + }; + + auto AppendNull = [&]() -> Status { + validity_builder.UnsafeAppend(false); + return visit_null(); + }; + + auto AppendMaybeNull = [&](int64_t index) -> Status { + if (values_is_valid[index]) { + return AppendNotNull(index); + } else { + return AppendNull(); + } + }; + + while (in_position < selection->length) { + BitBlockCount filter_valid_block = filter_valid_counter.NextWord(); + BitBlockCount values_valid_block = values_valid_counter.NextWord(); + BitBlockCount filter_block = filter_counter.NextWord(); + if (filter_block.NoneSet() && null_selection == FilterOptions::DROP) { + // For this exceedingly common case in low-selectivity filters we can + // skip further analysis of the data and move on to the next block. + in_position += filter_block.length; + } else if (filter_valid_block.AllSet()) { + // Simpler path: no filter values are null + if (filter_block.AllSet()) { + // Fastest path: filter values are all true and not null + if (values_valid_block.AllSet()) { + // The values aren't null either + validity_builder.UnsafeAppend(filter_block.length, true); + for (int64_t i = 0; i < filter_block.length; ++i) { + RETURN_NOT_OK(visit_valid(in_position++)); + } + } else { + // Some of the values in this block are null + for (int64_t i = 0; i < filter_block.length; ++i) { + RETURN_NOT_OK(AppendMaybeNull(in_position++)); + } + } + } else { // !filter_block.AllSet() + // Some of the filter values are false, but all not null + if (values_valid_block.AllSet()) { + // All the values are not-null, so we can skip null checking for + // them + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { + RETURN_NOT_OK(AppendNotNull(in_position)); + } + ++in_position; + } + } else { + // Some of the values in the block are null, so we have to check + // each one + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_data, filter_offset + in_position)) { + RETURN_NOT_OK(AppendMaybeNull(in_position)); + } + ++in_position; + } + } + } + } else { // !filter_valid_block.AllSet() + // Some of the filter values are null, so we have to handle the DROP + // versus EMIT_NULL null selection behavior. + if (null_selection == FilterOptions::DROP) { + // Filter null values are treated as false. + for (int64_t i = 0; i < filter_block.length; ++i) { + if (BitUtil::GetBit(filter_is_valid, filter_offset + in_position) && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + RETURN_NOT_OK(AppendMaybeNull(in_position)); + } + ++in_position; + } + } else { + // Filter null values are appended to output as null whether the + // value in the corresponding slot is valid or not + for (int64_t i = 0; i < filter_block.length; ++i) { + const bool filter_not_null = + BitUtil::GetBit(filter_is_valid, filter_offset + in_position); + if (filter_not_null && + BitUtil::GetBit(filter_data, filter_offset + in_position)) { + RETURN_NOT_OK(AppendMaybeNull(in_position)); + } else if (!filter_not_null) { + // EMIT_NULL case + RETURN_NOT_OK(AppendNull()); + } + ++in_position; + } + } + } + } + return Status::OK(); + } + + virtual Status Init() { return Status::OK(); } + + // Implementation specific finish logic + virtual Status Finish() = 0; + + Status ExecTake() { + RETURN_NOT_OK(this->validity_builder.Reserve(output_length)); + RETURN_NOT_OK(Init()); + int index_width = GetByteWidth(*this->selection->type); + + // CTRP dispatch here + switch (index_width) { + case 1: { + Status s = + static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint8_t>>(); + RETURN_NOT_OK(s); + } break; + case 2: { + Status s = + static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint16_t>>(); + RETURN_NOT_OK(s); + } break; + case 4: { + Status s = + static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint32_t>>(); + RETURN_NOT_OK(s); + } break; + case 8: { + Status s = + static_cast<Impl*>(this)->template GenerateOutput<TakeAdapter<uint64_t>>(); + RETURN_NOT_OK(s); + } break; + default: + DCHECK(false) << "Invalid index width"; + break; + } + RETURN_NOT_OK(this->FinishCommon()); + return Finish(); + } + + Status ExecFilter() { + RETURN_NOT_OK(this->validity_builder.Reserve(output_length)); + RETURN_NOT_OK(Init()); + // CRTP dispatch + Status s = static_cast<Impl*>(this)->template GenerateOutput<FilterAdapter>(); + RETURN_NOT_OK(s); + RETURN_NOT_OK(this->FinishCommon()); + return Finish(); + } +}; + +#define LIFT_BASE_MEMBERS() \ + using ValuesArrayType = typename Base::ValuesArrayType; \ + using Base::ctx; \ + using Base::values; \ + using Base::selection; \ + using Base::output_length; \ + using Base::out; \ + using Base::validity_builder + +static inline Status VisitNoop() { return Status::OK(); } + +// A selection implementation for 32-bit and 64-bit variable binary +// types. Common generated kernels are shared between Binary/String and +// LargeBinary/LargeString +template <typename Type> +struct VarBinaryImpl : public Selection<VarBinaryImpl<Type>, Type> { + using offset_type = typename Type::offset_type; + + using Base = Selection<VarBinaryImpl<Type>, Type>; + LIFT_BASE_MEMBERS(); + + std::shared_ptr<ArrayData> values_as_binary; + TypedBufferBuilder<offset_type> offset_builder; + TypedBufferBuilder<uint8_t> data_builder; + + static constexpr int64_t kOffsetLimit = std::numeric_limits<offset_type>::max() - 1; + + VarBinaryImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, + Datum* out) + : Base(ctx, batch, output_length, out), + offset_builder(ctx->memory_pool()), + data_builder(ctx->memory_pool()) {} + + template <typename Adapter> + Status GenerateOutput() { + ValuesArrayType typed_values(this->values_as_binary); + + // Presize the data builder with a rough estimate of the required data size + if (values->length > 0) { + const double mean_value_length = + (typed_values.total_values_length() / static_cast<double>(values->length)); + + // TODO: See if possible to reduce output_length for take/filter cases + // where there are nulls in the selection array + RETURN_NOT_OK( + data_builder.Reserve(static_cast<int64_t>(mean_value_length * output_length))); + } + int64_t space_available = data_builder.capacity(); + + const offset_type* raw_offsets = typed_values.raw_value_offsets(); + const uint8_t* raw_data = typed_values.raw_data(); + + offset_type offset = 0; + Adapter adapter(this); + RETURN_NOT_OK(adapter.Generate( + [&](int64_t index) { + offset_builder.UnsafeAppend(offset); + offset_type val_offset = raw_offsets[index]; + offset_type val_size = raw_offsets[index + 1] - val_offset; + + // Use static property to prune this code from the filter path in + // optimized builds + if (Adapter::is_take && + ARROW_PREDICT_FALSE(static_cast<int64_t>(offset) + + static_cast<int64_t>(val_size)) > kOffsetLimit) { + return Status::Invalid("Take operation overflowed binary array capacity"); + } + offset += val_size; + if (ARROW_PREDICT_FALSE(val_size > space_available)) { + RETURN_NOT_OK(data_builder.Reserve(val_size)); + space_available = data_builder.capacity() - data_builder.length(); + } + data_builder.UnsafeAppend(raw_data + val_offset, val_size); + space_available -= val_size; + return Status::OK(); + }, + [&]() { + offset_builder.UnsafeAppend(offset); + return Status::OK(); + })); + offset_builder.UnsafeAppend(offset); + return Status::OK(); + } + + Status Init() override { + ARROW_ASSIGN_OR_RAISE(this->values_as_binary, + GetArrayView(this->values, TypeTraits<Type>::type_singleton())); + return offset_builder.Reserve(output_length + 1); + } + + Status Finish() override { + RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); + return data_builder.Finish(&out->buffers[2]); + } +}; + +struct FSBImpl : public Selection<FSBImpl, FixedSizeBinaryType> { + using Base = Selection<FSBImpl, FixedSizeBinaryType>; + LIFT_BASE_MEMBERS(); + + TypedBufferBuilder<uint8_t> data_builder; + + FSBImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) + : Base(ctx, batch, output_length, out), data_builder(ctx->memory_pool()) {} + + template <typename Adapter> + Status GenerateOutput() { + FixedSizeBinaryArray typed_values(this->values); + int32_t value_size = typed_values.byte_width(); + + RETURN_NOT_OK(data_builder.Reserve(value_size * output_length)); + Adapter adapter(this); + return adapter.Generate( + [&](int64_t index) { + auto val = typed_values.GetView(index); + data_builder.UnsafeAppend(reinterpret_cast<const uint8_t*>(val.data()), + value_size); + return Status::OK(); + }, + [&]() { + data_builder.UnsafeAppend(value_size, static_cast<uint8_t>(0x00)); + return Status::OK(); + }); + } + + Status Finish() override { return data_builder.Finish(&out->buffers[1]); } +}; + +template <typename Type> +struct ListImpl : public Selection<ListImpl<Type>, Type> { + using offset_type = typename Type::offset_type; + + using Base = Selection<ListImpl<Type>, Type>; + LIFT_BASE_MEMBERS(); + + TypedBufferBuilder<offset_type> offset_builder; + typename TypeTraits<Type>::OffsetBuilderType child_index_builder; + + ListImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) + : Base(ctx, batch, output_length, out), + offset_builder(ctx->memory_pool()), + child_index_builder(ctx->memory_pool()) {} + + template <typename Adapter> + Status GenerateOutput() { + ValuesArrayType typed_values(this->values); + + // TODO presize child_index_builder with a similar heuristic as VarBinaryImpl + + offset_type offset = 0; + Adapter adapter(this); + RETURN_NOT_OK(adapter.Generate( + [&](int64_t index) { + offset_builder.UnsafeAppend(offset); + offset_type value_offset = typed_values.value_offset(index); + offset_type value_length = typed_values.value_length(index); + offset += value_length; + RETURN_NOT_OK(child_index_builder.Reserve(value_length)); + for (offset_type j = value_offset; j < value_offset + value_length; ++j) { + child_index_builder.UnsafeAppend(j); + } + return Status::OK(); + }, + [&]() { + offset_builder.UnsafeAppend(offset); + return Status::OK(); + })); + offset_builder.UnsafeAppend(offset); + return Status::OK(); + } + + Status Init() override { + RETURN_NOT_OK(offset_builder.Reserve(output_length + 1)); + return Status::OK(); + } + + Status Finish() override { + std::shared_ptr<Array> child_indices; + RETURN_NOT_OK(child_index_builder.Finish(&child_indices)); + + ValuesArrayType typed_values(this->values); + + // No need to boundscheck the child values indices + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Array> taken_child, + Take(*typed_values.values(), *child_indices, + TakeOptions::NoBoundsCheck(), ctx->exec_context())); + RETURN_NOT_OK(offset_builder.Finish(&out->buffers[1])); + out->child_data = {taken_child->data()}; + return Status::OK(); + } +}; + struct DenseUnionImpl : public Selection<DenseUnionImpl, DenseUnionType> { using Base = Selection<DenseUnionImpl, DenseUnionType>; LIFT_BASE_MEMBERS(); @@ -1743,144 +1743,144 @@ struct DenseUnionImpl : public Selection<DenseUnionImpl, DenseUnionType> { } }; -struct FSLImpl : public Selection<FSLImpl, FixedSizeListType> { - Int64Builder child_index_builder; - - using Base = Selection<FSLImpl, FixedSizeListType>; - LIFT_BASE_MEMBERS(); - - FSLImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) - : Base(ctx, batch, output_length, out), child_index_builder(ctx->memory_pool()) {} - - template <typename Adapter> - Status GenerateOutput() { - ValuesArrayType typed_values(this->values); - int32_t list_size = typed_values.list_type()->list_size(); - - /// We must take list_size elements even for null elements of - /// indices. - RETURN_NOT_OK(child_index_builder.Reserve(output_length * list_size)); - - Adapter adapter(this); - return adapter.Generate( - [&](int64_t index) { - int64_t offset = index * list_size; - for (int64_t j = offset; j < offset + list_size; ++j) { - child_index_builder.UnsafeAppend(j); - } - return Status::OK(); - }, - [&]() { return child_index_builder.AppendNulls(list_size); }); - } - - Status Finish() override { - std::shared_ptr<Array> child_indices; - RETURN_NOT_OK(child_index_builder.Finish(&child_indices)); - - ValuesArrayType typed_values(this->values); - - // No need to boundscheck the child values indices - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Array> taken_child, - Take(*typed_values.values(), *child_indices, - TakeOptions::NoBoundsCheck(), ctx->exec_context())); - out->child_data = {taken_child->data()}; - return Status::OK(); - } -}; - -// ---------------------------------------------------------------------- -// Struct selection implementations - -// We need a slightly different approach for StructType. For Take, we can -// invoke Take on each struct field's data with boundschecking disabled. For -// Filter on the other hand, if we naively call Filter on each field, then the -// filter output length will have to be redundantly computed. Thus, for Filter -// we instead convert the filter to selection indices and then invoke take. - -// Struct selection implementation. ONLY used for Take -struct StructImpl : public Selection<StructImpl, StructType> { - using Base = Selection<StructImpl, StructType>; - LIFT_BASE_MEMBERS(); - using Base::Base; - - template <typename Adapter> - Status GenerateOutput() { - StructArray typed_values(values); - Adapter adapter(this); - // There's nothing to do for Struct except to generate the validity bitmap - return adapter.Generate([&](int64_t index) { return Status::OK(); }, - /*visit_null=*/VisitNoop); - } - - Status Finish() override { - StructArray typed_values(values); - - // Select from children without boundschecking - out->child_data.resize(values->type->num_fields()); - for (int field_index = 0; field_index < values->type->num_fields(); ++field_index) { - ARROW_ASSIGN_OR_RAISE(Datum taken_field, - Take(Datum(typed_values.field(field_index)), Datum(selection), - TakeOptions::NoBoundsCheck(), ctx->exec_context())); - out->child_data[field_index] = taken_field.array(); - } - return Status::OK(); - } -}; - +struct FSLImpl : public Selection<FSLImpl, FixedSizeListType> { + Int64Builder child_index_builder; + + using Base = Selection<FSLImpl, FixedSizeListType>; + LIFT_BASE_MEMBERS(); + + FSLImpl(KernelContext* ctx, const ExecBatch& batch, int64_t output_length, Datum* out) + : Base(ctx, batch, output_length, out), child_index_builder(ctx->memory_pool()) {} + + template <typename Adapter> + Status GenerateOutput() { + ValuesArrayType typed_values(this->values); + int32_t list_size = typed_values.list_type()->list_size(); + + /// We must take list_size elements even for null elements of + /// indices. + RETURN_NOT_OK(child_index_builder.Reserve(output_length * list_size)); + + Adapter adapter(this); + return adapter.Generate( + [&](int64_t index) { + int64_t offset = index * list_size; + for (int64_t j = offset; j < offset + list_size; ++j) { + child_index_builder.UnsafeAppend(j); + } + return Status::OK(); + }, + [&]() { return child_index_builder.AppendNulls(list_size); }); + } + + Status Finish() override { + std::shared_ptr<Array> child_indices; + RETURN_NOT_OK(child_index_builder.Finish(&child_indices)); + + ValuesArrayType typed_values(this->values); + + // No need to boundscheck the child values indices + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Array> taken_child, + Take(*typed_values.values(), *child_indices, + TakeOptions::NoBoundsCheck(), ctx->exec_context())); + out->child_data = {taken_child->data()}; + return Status::OK(); + } +}; + +// ---------------------------------------------------------------------- +// Struct selection implementations + +// We need a slightly different approach for StructType. For Take, we can +// invoke Take on each struct field's data with boundschecking disabled. For +// Filter on the other hand, if we naively call Filter on each field, then the +// filter output length will have to be redundantly computed. Thus, for Filter +// we instead convert the filter to selection indices and then invoke take. + +// Struct selection implementation. ONLY used for Take +struct StructImpl : public Selection<StructImpl, StructType> { + using Base = Selection<StructImpl, StructType>; + LIFT_BASE_MEMBERS(); + using Base::Base; + + template <typename Adapter> + Status GenerateOutput() { + StructArray typed_values(values); + Adapter adapter(this); + // There's nothing to do for Struct except to generate the validity bitmap + return adapter.Generate([&](int64_t index) { return Status::OK(); }, + /*visit_null=*/VisitNoop); + } + + Status Finish() override { + StructArray typed_values(values); + + // Select from children without boundschecking + out->child_data.resize(values->type->num_fields()); + for (int field_index = 0; field_index < values->type->num_fields(); ++field_index) { + ARROW_ASSIGN_OR_RAISE(Datum taken_field, + Take(Datum(typed_values.field(field_index)), Datum(selection), + TakeOptions::NoBoundsCheck(), ctx->exec_context())); + out->child_data[field_index] = taken_field.array(); + } + return Status::OK(); + } +}; + Status StructFilter(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - // Transform filter to selection indices and then use Take. - std::shared_ptr<ArrayData> indices; + // Transform filter to selection indices and then use Take. + std::shared_ptr<ArrayData> indices; RETURN_NOT_OK(GetTakeIndices(*batch[1].array(), FilterState::Get(ctx).null_selection_behavior, ctx->memory_pool()) .Value(&indices)); - - Datum result; + + Datum result; RETURN_NOT_OK( Take(batch[0], Datum(indices), TakeOptions::NoBoundsCheck(), ctx->exec_context()) .Value(&result)); - out->value = result.array(); + out->value = result.array(); return Status::OK(); -} - -#undef LIFT_BASE_MEMBERS - -// ---------------------------------------------------------------------- -// Implement Filter metafunction - -Result<std::shared_ptr<RecordBatch>> FilterRecordBatch(const RecordBatch& batch, - const Datum& filter, - const FunctionOptions* options, - ExecContext* ctx) { - if (batch.num_rows() != filter.length()) { - return Status::Invalid("Filter inputs must all be the same length"); - } - - // Convert filter to selection vector/indices and use Take - const auto& filter_opts = *static_cast<const FilterOptions*>(options); - ARROW_ASSIGN_OR_RAISE( - std::shared_ptr<ArrayData> indices, +} + +#undef LIFT_BASE_MEMBERS + +// ---------------------------------------------------------------------- +// Implement Filter metafunction + +Result<std::shared_ptr<RecordBatch>> FilterRecordBatch(const RecordBatch& batch, + const Datum& filter, + const FunctionOptions* options, + ExecContext* ctx) { + if (batch.num_rows() != filter.length()) { + return Status::Invalid("Filter inputs must all be the same length"); + } + + // Convert filter to selection vector/indices and use Take + const auto& filter_opts = *static_cast<const FilterOptions*>(options); + ARROW_ASSIGN_OR_RAISE( + std::shared_ptr<ArrayData> indices, GetTakeIndices(*filter.array(), filter_opts.null_selection_behavior, ctx->memory_pool())); - std::vector<std::shared_ptr<Array>> columns(batch.num_columns()); - for (int i = 0; i < batch.num_columns(); ++i) { - ARROW_ASSIGN_OR_RAISE(Datum out, Take(batch.column(i)->data(), Datum(indices), - TakeOptions::NoBoundsCheck(), ctx)); - columns[i] = out.make_array(); - } + std::vector<std::shared_ptr<Array>> columns(batch.num_columns()); + for (int i = 0; i < batch.num_columns(); ++i) { + ARROW_ASSIGN_OR_RAISE(Datum out, Take(batch.column(i)->data(), Datum(indices), + TakeOptions::NoBoundsCheck(), ctx)); + columns[i] = out.make_array(); + } return RecordBatch::Make(batch.schema(), indices->length, std::move(columns)); -} - -Result<std::shared_ptr<Table>> FilterTable(const Table& table, const Datum& filter, - const FunctionOptions* options, - ExecContext* ctx) { - if (table.num_rows() != filter.length()) { - return Status::Invalid("Filter inputs must all be the same length"); - } +} + +Result<std::shared_ptr<Table>> FilterTable(const Table& table, const Datum& filter, + const FunctionOptions* options, + ExecContext* ctx) { + if (table.num_rows() != filter.length()) { + return Status::Invalid("Filter inputs must all be the same length"); + } if (table.num_rows() == 0) { return Table::Make(table.schema(), table.columns(), 0); } - + // Last input element will be the filter array const int num_columns = table.num_columns(); std::vector<ArrayVector> inputs(num_columns + 1); @@ -1914,7 +1914,7 @@ Result<std::shared_ptr<Table>> FilterTable(const Table& table, const Datum& filt for (int64_t i = 0; i < num_chunks; ++i) { const ArrayData& filter_chunk = *inputs.back()[i]->data(); - ARROW_ASSIGN_OR_RAISE( + ARROW_ASSIGN_OR_RAISE( const auto indices, GetTakeIndices(filter_chunk, filter_opts.null_selection_behavior, ctx->memory_pool())); @@ -1930,7 +1930,7 @@ Result<std::shared_ptr<Table>> FilterTable(const Table& table, const Datum& filt } out_num_rows += indices->length; } - } + } ChunkedArrayVector out_chunks(num_columns); for (int i = 0; i < num_columns; ++i) { @@ -1938,10 +1938,10 @@ Result<std::shared_ptr<Table>> FilterTable(const Table& table, const Datum& filt table.column(i)->type()); } return Table::Make(table.schema(), std::move(out_chunks), out_num_rows); -} - -static auto kDefaultFilterOptions = FilterOptions::Defaults(); - +} + +static auto kDefaultFilterOptions = FilterOptions::Defaults(); + const FunctionDoc filter_doc( "Filter with a boolean selection filter", ("The output is populated with values from the input at positions\n" @@ -1949,244 +1949,244 @@ const FunctionDoc filter_doc( "are handled based on FilterOptions."), {"input", "selection_filter"}, "FilterOptions"); -class FilterMetaFunction : public MetaFunction { - public: - FilterMetaFunction() +class FilterMetaFunction : public MetaFunction { + public: + FilterMetaFunction() : MetaFunction("filter", Arity::Binary(), &filter_doc, &kDefaultFilterOptions) {} - - Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const override { - if (args[1].type()->id() != Type::BOOL) { - return Status::NotImplemented("Filter argument must be boolean type"); - } - - if (args[0].kind() == Datum::RECORD_BATCH) { - auto values_batch = args[0].record_batch(); - ARROW_ASSIGN_OR_RAISE( - std::shared_ptr<RecordBatch> out_batch, - FilterRecordBatch(*args[0].record_batch(), args[1], options, ctx)); - return Datum(out_batch); - } else if (args[0].kind() == Datum::TABLE) { - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Table> out_table, - FilterTable(*args[0].table(), args[1], options, ctx)); - return Datum(out_table); - } else { - return CallFunction("array_filter", args, options, ctx); - } - } -}; - -// ---------------------------------------------------------------------- -// Implement Take metafunction - -// Shorthand naming of these functions -// A -> Array -// C -> ChunkedArray -// R -> RecordBatch -// T -> Table - -Result<std::shared_ptr<Array>> TakeAA(const Array& values, const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - ARROW_ASSIGN_OR_RAISE(Datum result, - CallFunction("array_take", {values, indices}, &options, ctx)); - return result.make_array(); -} - -Result<std::shared_ptr<ChunkedArray>> TakeCA(const ChunkedArray& values, - const Array& indices, - const TakeOptions& options, - ExecContext* ctx) { - auto num_chunks = values.num_chunks(); - std::vector<std::shared_ptr<Array>> new_chunks(1); // Hard-coded 1 for now - std::shared_ptr<Array> current_chunk; - - // Case 1: `values` has a single chunk, so just use it - if (num_chunks == 1) { - current_chunk = values.chunk(0); - } else { - // TODO Case 2: See if all `indices` fall in the same chunk and call Array Take on it - // See - // https://github.com/apache/arrow/blob/6f2c9041137001f7a9212f244b51bc004efc29af/r/src/compute.cpp#L123-L151 - // TODO Case 3: If indices are sorted, can slice them and call Array Take - - // Case 4: Else, concatenate chunks and call Array Take - ARROW_ASSIGN_OR_RAISE(current_chunk, - Concatenate(values.chunks(), ctx->memory_pool())); - } - // Call Array Take on our single chunk - ARROW_ASSIGN_OR_RAISE(new_chunks[0], TakeAA(*current_chunk, indices, options, ctx)); - return std::make_shared<ChunkedArray>(std::move(new_chunks)); -} - -Result<std::shared_ptr<ChunkedArray>> TakeCC(const ChunkedArray& values, - const ChunkedArray& indices, - const TakeOptions& options, - ExecContext* ctx) { - auto num_chunks = indices.num_chunks(); - std::vector<std::shared_ptr<Array>> new_chunks(num_chunks); - for (int i = 0; i < num_chunks; i++) { - // Take with that indices chunk - // Note that as currently implemented, this is inefficient because `values` - // will get concatenated on every iteration of this loop - ARROW_ASSIGN_OR_RAISE(std::shared_ptr<ChunkedArray> current_chunk, - TakeCA(values, *indices.chunk(i), options, ctx)); - // Concatenate the result to make a single array for this chunk - ARROW_ASSIGN_OR_RAISE(new_chunks[i], - Concatenate(current_chunk->chunks(), ctx->memory_pool())); - } - return std::make_shared<ChunkedArray>(std::move(new_chunks)); -} - -Result<std::shared_ptr<ChunkedArray>> TakeAC(const Array& values, - const ChunkedArray& indices, - const TakeOptions& options, - ExecContext* ctx) { - auto num_chunks = indices.num_chunks(); - std::vector<std::shared_ptr<Array>> new_chunks(num_chunks); - for (int i = 0; i < num_chunks; i++) { - // Take with that indices chunk - ARROW_ASSIGN_OR_RAISE(new_chunks[i], TakeAA(values, *indices.chunk(i), options, ctx)); - } - return std::make_shared<ChunkedArray>(std::move(new_chunks)); -} - -Result<std::shared_ptr<RecordBatch>> TakeRA(const RecordBatch& batch, - const Array& indices, - const TakeOptions& options, - ExecContext* ctx) { - auto ncols = batch.num_columns(); - auto nrows = indices.length(); - std::vector<std::shared_ptr<Array>> columns(ncols); - for (int j = 0; j < ncols; j++) { - ARROW_ASSIGN_OR_RAISE(columns[j], TakeAA(*batch.column(j), indices, options, ctx)); - } + + Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const override { + if (args[1].type()->id() != Type::BOOL) { + return Status::NotImplemented("Filter argument must be boolean type"); + } + + if (args[0].kind() == Datum::RECORD_BATCH) { + auto values_batch = args[0].record_batch(); + ARROW_ASSIGN_OR_RAISE( + std::shared_ptr<RecordBatch> out_batch, + FilterRecordBatch(*args[0].record_batch(), args[1], options, ctx)); + return Datum(out_batch); + } else if (args[0].kind() == Datum::TABLE) { + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Table> out_table, + FilterTable(*args[0].table(), args[1], options, ctx)); + return Datum(out_table); + } else { + return CallFunction("array_filter", args, options, ctx); + } + } +}; + +// ---------------------------------------------------------------------- +// Implement Take metafunction + +// Shorthand naming of these functions +// A -> Array +// C -> ChunkedArray +// R -> RecordBatch +// T -> Table + +Result<std::shared_ptr<Array>> TakeAA(const Array& values, const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + ARROW_ASSIGN_OR_RAISE(Datum result, + CallFunction("array_take", {values, indices}, &options, ctx)); + return result.make_array(); +} + +Result<std::shared_ptr<ChunkedArray>> TakeCA(const ChunkedArray& values, + const Array& indices, + const TakeOptions& options, + ExecContext* ctx) { + auto num_chunks = values.num_chunks(); + std::vector<std::shared_ptr<Array>> new_chunks(1); // Hard-coded 1 for now + std::shared_ptr<Array> current_chunk; + + // Case 1: `values` has a single chunk, so just use it + if (num_chunks == 1) { + current_chunk = values.chunk(0); + } else { + // TODO Case 2: See if all `indices` fall in the same chunk and call Array Take on it + // See + // https://github.com/apache/arrow/blob/6f2c9041137001f7a9212f244b51bc004efc29af/r/src/compute.cpp#L123-L151 + // TODO Case 3: If indices are sorted, can slice them and call Array Take + + // Case 4: Else, concatenate chunks and call Array Take + ARROW_ASSIGN_OR_RAISE(current_chunk, + Concatenate(values.chunks(), ctx->memory_pool())); + } + // Call Array Take on our single chunk + ARROW_ASSIGN_OR_RAISE(new_chunks[0], TakeAA(*current_chunk, indices, options, ctx)); + return std::make_shared<ChunkedArray>(std::move(new_chunks)); +} + +Result<std::shared_ptr<ChunkedArray>> TakeCC(const ChunkedArray& values, + const ChunkedArray& indices, + const TakeOptions& options, + ExecContext* ctx) { + auto num_chunks = indices.num_chunks(); + std::vector<std::shared_ptr<Array>> new_chunks(num_chunks); + for (int i = 0; i < num_chunks; i++) { + // Take with that indices chunk + // Note that as currently implemented, this is inefficient because `values` + // will get concatenated on every iteration of this loop + ARROW_ASSIGN_OR_RAISE(std::shared_ptr<ChunkedArray> current_chunk, + TakeCA(values, *indices.chunk(i), options, ctx)); + // Concatenate the result to make a single array for this chunk + ARROW_ASSIGN_OR_RAISE(new_chunks[i], + Concatenate(current_chunk->chunks(), ctx->memory_pool())); + } + return std::make_shared<ChunkedArray>(std::move(new_chunks)); +} + +Result<std::shared_ptr<ChunkedArray>> TakeAC(const Array& values, + const ChunkedArray& indices, + const TakeOptions& options, + ExecContext* ctx) { + auto num_chunks = indices.num_chunks(); + std::vector<std::shared_ptr<Array>> new_chunks(num_chunks); + for (int i = 0; i < num_chunks; i++) { + // Take with that indices chunk + ARROW_ASSIGN_OR_RAISE(new_chunks[i], TakeAA(values, *indices.chunk(i), options, ctx)); + } + return std::make_shared<ChunkedArray>(std::move(new_chunks)); +} + +Result<std::shared_ptr<RecordBatch>> TakeRA(const RecordBatch& batch, + const Array& indices, + const TakeOptions& options, + ExecContext* ctx) { + auto ncols = batch.num_columns(); + auto nrows = indices.length(); + std::vector<std::shared_ptr<Array>> columns(ncols); + for (int j = 0; j < ncols; j++) { + ARROW_ASSIGN_OR_RAISE(columns[j], TakeAA(*batch.column(j), indices, options, ctx)); + } return RecordBatch::Make(batch.schema(), nrows, std::move(columns)); -} - -Result<std::shared_ptr<Table>> TakeTA(const Table& table, const Array& indices, - const TakeOptions& options, ExecContext* ctx) { - auto ncols = table.num_columns(); - std::vector<std::shared_ptr<ChunkedArray>> columns(ncols); - - for (int j = 0; j < ncols; j++) { - ARROW_ASSIGN_OR_RAISE(columns[j], TakeCA(*table.column(j), indices, options, ctx)); - } +} + +Result<std::shared_ptr<Table>> TakeTA(const Table& table, const Array& indices, + const TakeOptions& options, ExecContext* ctx) { + auto ncols = table.num_columns(); + std::vector<std::shared_ptr<ChunkedArray>> columns(ncols); + + for (int j = 0; j < ncols; j++) { + ARROW_ASSIGN_OR_RAISE(columns[j], TakeCA(*table.column(j), indices, options, ctx)); + } return Table::Make(table.schema(), std::move(columns)); -} - -Result<std::shared_ptr<Table>> TakeTC(const Table& table, const ChunkedArray& indices, - const TakeOptions& options, ExecContext* ctx) { - auto ncols = table.num_columns(); - std::vector<std::shared_ptr<ChunkedArray>> columns(ncols); - for (int j = 0; j < ncols; j++) { - ARROW_ASSIGN_OR_RAISE(columns[j], TakeCC(*table.column(j), indices, options, ctx)); - } +} + +Result<std::shared_ptr<Table>> TakeTC(const Table& table, const ChunkedArray& indices, + const TakeOptions& options, ExecContext* ctx) { + auto ncols = table.num_columns(); + std::vector<std::shared_ptr<ChunkedArray>> columns(ncols); + for (int j = 0; j < ncols; j++) { + ARROW_ASSIGN_OR_RAISE(columns[j], TakeCC(*table.column(j), indices, options, ctx)); + } return Table::Make(table.schema(), std::move(columns)); -} - -static auto kDefaultTakeOptions = TakeOptions::Defaults(); - +} + +static auto kDefaultTakeOptions = TakeOptions::Defaults(); + const FunctionDoc take_doc( "Select values from an input based on indices from another array", ("The output is populated with values from the input at positions\n" "given by `indices`. Nulls in `indices` emit null in the output."), {"input", "indices"}, "TakeOptions"); -// Metafunction for dispatching to different Take implementations other than -// Array-Array. -// -// TODO: Revamp approach to executing Take operations. In addition to being -// overly complex dispatching, there is no parallelization. -class TakeMetaFunction : public MetaFunction { - public: +// Metafunction for dispatching to different Take implementations other than +// Array-Array. +// +// TODO: Revamp approach to executing Take operations. In addition to being +// overly complex dispatching, there is no parallelization. +class TakeMetaFunction : public MetaFunction { + public: TakeMetaFunction() : MetaFunction("take", Arity::Binary(), &take_doc, &kDefaultTakeOptions) {} - - Result<Datum> ExecuteImpl(const std::vector<Datum>& args, - const FunctionOptions* options, - ExecContext* ctx) const override { - Datum::Kind index_kind = args[1].kind(); - const TakeOptions& take_opts = static_cast<const TakeOptions&>(*options); - switch (args[0].kind()) { - case Datum::ARRAY: - if (index_kind == Datum::ARRAY) { - return TakeAA(*args[0].make_array(), *args[1].make_array(), take_opts, ctx); - } else if (index_kind == Datum::CHUNKED_ARRAY) { - return TakeAC(*args[0].make_array(), *args[1].chunked_array(), take_opts, ctx); - } - break; - case Datum::CHUNKED_ARRAY: - if (index_kind == Datum::ARRAY) { - return TakeCA(*args[0].chunked_array(), *args[1].make_array(), take_opts, ctx); - } else if (index_kind == Datum::CHUNKED_ARRAY) { - return TakeCC(*args[0].chunked_array(), *args[1].chunked_array(), take_opts, - ctx); - } - break; - case Datum::RECORD_BATCH: - if (index_kind == Datum::ARRAY) { - return TakeRA(*args[0].record_batch(), *args[1].make_array(), take_opts, ctx); - } - break; - case Datum::TABLE: - if (index_kind == Datum::ARRAY) { - return TakeTA(*args[0].table(), *args[1].make_array(), take_opts, ctx); - } else if (index_kind == Datum::CHUNKED_ARRAY) { - return TakeTC(*args[0].table(), *args[1].chunked_array(), take_opts, ctx); - } - break; - default: - break; - } - return Status::NotImplemented( - "Unsupported types for take operation: " - "values=", - args[0].ToString(), "indices=", args[1].ToString()); - } -}; - -// ---------------------------------------------------------------------- - -template <typename Impl> + + Result<Datum> ExecuteImpl(const std::vector<Datum>& args, + const FunctionOptions* options, + ExecContext* ctx) const override { + Datum::Kind index_kind = args[1].kind(); + const TakeOptions& take_opts = static_cast<const TakeOptions&>(*options); + switch (args[0].kind()) { + case Datum::ARRAY: + if (index_kind == Datum::ARRAY) { + return TakeAA(*args[0].make_array(), *args[1].make_array(), take_opts, ctx); + } else if (index_kind == Datum::CHUNKED_ARRAY) { + return TakeAC(*args[0].make_array(), *args[1].chunked_array(), take_opts, ctx); + } + break; + case Datum::CHUNKED_ARRAY: + if (index_kind == Datum::ARRAY) { + return TakeCA(*args[0].chunked_array(), *args[1].make_array(), take_opts, ctx); + } else if (index_kind == Datum::CHUNKED_ARRAY) { + return TakeCC(*args[0].chunked_array(), *args[1].chunked_array(), take_opts, + ctx); + } + break; + case Datum::RECORD_BATCH: + if (index_kind == Datum::ARRAY) { + return TakeRA(*args[0].record_batch(), *args[1].make_array(), take_opts, ctx); + } + break; + case Datum::TABLE: + if (index_kind == Datum::ARRAY) { + return TakeTA(*args[0].table(), *args[1].make_array(), take_opts, ctx); + } else if (index_kind == Datum::CHUNKED_ARRAY) { + return TakeTC(*args[0].table(), *args[1].chunked_array(), take_opts, ctx); + } + break; + default: + break; + } + return Status::NotImplemented( + "Unsupported types for take operation: " + "values=", + args[0].ToString(), "indices=", args[1].ToString()); + } +}; + +// ---------------------------------------------------------------------- + +template <typename Impl> Status FilterExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - // TODO: where are the values and filter length equality checked? - int64_t output_length = GetFilterOutputSize( - *batch[1].array(), FilterState::Get(ctx).null_selection_behavior); - Impl kernel(ctx, batch, output_length, out); + // TODO: where are the values and filter length equality checked? + int64_t output_length = GetFilterOutputSize( + *batch[1].array(), FilterState::Get(ctx).null_selection_behavior); + Impl kernel(ctx, batch, output_length, out); return kernel.ExecFilter(); -} - -template <typename Impl> +} + +template <typename Impl> Status TakeExec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { - if (TakeState::Get(ctx).boundscheck) { + if (TakeState::Get(ctx).boundscheck) { RETURN_NOT_OK(CheckIndexBounds(*batch[1].array(), batch[0].length())); - } - Impl kernel(ctx, batch, /*output_length=*/batch[1].length(), out); + } + Impl kernel(ctx, batch, /*output_length=*/batch[1].length(), out); return kernel.ExecTake(); -} - -struct SelectionKernelDescr { - InputType input; - ArrayKernelExec exec; -}; - +} + +struct SelectionKernelDescr { + InputType input; + ArrayKernelExec exec; +}; + void RegisterSelectionFunction(const std::string& name, const FunctionDoc* doc, VectorKernel base_kernel, InputType selection_type, - const std::vector<SelectionKernelDescr>& descrs, - const FunctionOptions* default_options, - FunctionRegistry* registry) { + const std::vector<SelectionKernelDescr>& descrs, + const FunctionOptions* default_options, + FunctionRegistry* registry) { auto func = std::make_shared<VectorFunction>(name, Arity::Binary(), doc, default_options); - for (auto& descr : descrs) { - base_kernel.signature = KernelSignature::Make( - {std::move(descr.input), selection_type}, OutputType(FirstType)); - base_kernel.exec = descr.exec; - DCHECK_OK(func->AddKernel(base_kernel)); - } - DCHECK_OK(registry->AddFunction(std::move(func))); -} - + for (auto& descr : descrs) { + base_kernel.signature = KernelSignature::Make( + {std::move(descr.input), selection_type}, OutputType(FirstType)); + base_kernel.exec = descr.exec; + DCHECK_OK(func->AddKernel(base_kernel)); + } + DCHECK_OK(registry->AddFunction(std::move(func))); +} + const FunctionDoc array_filter_doc( "Filter with a boolean selection filter", ("The output is populated with values from the input `array` at positions\n" @@ -2200,69 +2200,69 @@ const FunctionDoc array_take_doc( "given by `indices`. Nulls in `indices` emit null in the output."), {"array", "indices"}, "TakeOptions"); -} // namespace - -void RegisterVectorSelection(FunctionRegistry* registry) { - // Filter kernels - std::vector<SelectionKernelDescr> filter_kernel_descrs = { - {InputType(match::Primitive(), ValueDescr::ARRAY), PrimitiveFilter}, - {InputType(match::BinaryLike(), ValueDescr::ARRAY), BinaryFilter}, - {InputType(match::LargeBinaryLike(), ValueDescr::ARRAY), BinaryFilter}, - {InputType::Array(Type::FIXED_SIZE_BINARY), FilterExec<FSBImpl>}, - {InputType::Array(null()), NullFilter}, - {InputType::Array(Type::DECIMAL), FilterExec<FSBImpl>}, - {InputType::Array(Type::DICTIONARY), DictionaryFilter}, - {InputType::Array(Type::EXTENSION), ExtensionFilter}, - {InputType::Array(Type::LIST), FilterExec<ListImpl<ListType>>}, - {InputType::Array(Type::LARGE_LIST), FilterExec<ListImpl<LargeListType>>}, - {InputType::Array(Type::FIXED_SIZE_LIST), FilterExec<FSLImpl>}, +} // namespace + +void RegisterVectorSelection(FunctionRegistry* registry) { + // Filter kernels + std::vector<SelectionKernelDescr> filter_kernel_descrs = { + {InputType(match::Primitive(), ValueDescr::ARRAY), PrimitiveFilter}, + {InputType(match::BinaryLike(), ValueDescr::ARRAY), BinaryFilter}, + {InputType(match::LargeBinaryLike(), ValueDescr::ARRAY), BinaryFilter}, + {InputType::Array(Type::FIXED_SIZE_BINARY), FilterExec<FSBImpl>}, + {InputType::Array(null()), NullFilter}, + {InputType::Array(Type::DECIMAL), FilterExec<FSBImpl>}, + {InputType::Array(Type::DICTIONARY), DictionaryFilter}, + {InputType::Array(Type::EXTENSION), ExtensionFilter}, + {InputType::Array(Type::LIST), FilterExec<ListImpl<ListType>>}, + {InputType::Array(Type::LARGE_LIST), FilterExec<ListImpl<LargeListType>>}, + {InputType::Array(Type::FIXED_SIZE_LIST), FilterExec<FSLImpl>}, {InputType::Array(Type::DENSE_UNION), FilterExec<DenseUnionImpl>}, - {InputType::Array(Type::STRUCT), StructFilter}, - // TODO: Reuse ListType kernel for MAP - {InputType::Array(Type::MAP), FilterExec<ListImpl<MapType>>}, - }; - - VectorKernel filter_base; - filter_base.init = FilterState::Init; + {InputType::Array(Type::STRUCT), StructFilter}, + // TODO: Reuse ListType kernel for MAP + {InputType::Array(Type::MAP), FilterExec<ListImpl<MapType>>}, + }; + + VectorKernel filter_base; + filter_base.init = FilterState::Init; RegisterSelectionFunction("array_filter", &array_filter_doc, filter_base, - /*selection_type=*/InputType::Array(boolean()), - filter_kernel_descrs, &kDefaultFilterOptions, registry); - - DCHECK_OK(registry->AddFunction(std::make_shared<FilterMetaFunction>())); - - // Take kernels - std::vector<SelectionKernelDescr> take_kernel_descrs = { - {InputType(match::Primitive(), ValueDescr::ARRAY), PrimitiveTake}, - {InputType(match::BinaryLike(), ValueDescr::ARRAY), - TakeExec<VarBinaryImpl<BinaryType>>}, - {InputType(match::LargeBinaryLike(), ValueDescr::ARRAY), - TakeExec<VarBinaryImpl<LargeBinaryType>>}, - {InputType::Array(Type::FIXED_SIZE_BINARY), TakeExec<FSBImpl>}, - {InputType::Array(null()), NullTake}, + /*selection_type=*/InputType::Array(boolean()), + filter_kernel_descrs, &kDefaultFilterOptions, registry); + + DCHECK_OK(registry->AddFunction(std::make_shared<FilterMetaFunction>())); + + // Take kernels + std::vector<SelectionKernelDescr> take_kernel_descrs = { + {InputType(match::Primitive(), ValueDescr::ARRAY), PrimitiveTake}, + {InputType(match::BinaryLike(), ValueDescr::ARRAY), + TakeExec<VarBinaryImpl<BinaryType>>}, + {InputType(match::LargeBinaryLike(), ValueDescr::ARRAY), + TakeExec<VarBinaryImpl<LargeBinaryType>>}, + {InputType::Array(Type::FIXED_SIZE_BINARY), TakeExec<FSBImpl>}, + {InputType::Array(null()), NullTake}, {InputType::Array(Type::DECIMAL128), TakeExec<FSBImpl>}, {InputType::Array(Type::DECIMAL256), TakeExec<FSBImpl>}, - {InputType::Array(Type::DICTIONARY), DictionaryTake}, - {InputType::Array(Type::EXTENSION), ExtensionTake}, - {InputType::Array(Type::LIST), TakeExec<ListImpl<ListType>>}, - {InputType::Array(Type::LARGE_LIST), TakeExec<ListImpl<LargeListType>>}, - {InputType::Array(Type::FIXED_SIZE_LIST), TakeExec<FSLImpl>}, + {InputType::Array(Type::DICTIONARY), DictionaryTake}, + {InputType::Array(Type::EXTENSION), ExtensionTake}, + {InputType::Array(Type::LIST), TakeExec<ListImpl<ListType>>}, + {InputType::Array(Type::LARGE_LIST), TakeExec<ListImpl<LargeListType>>}, + {InputType::Array(Type::FIXED_SIZE_LIST), TakeExec<FSLImpl>}, {InputType::Array(Type::DENSE_UNION), TakeExec<DenseUnionImpl>}, - {InputType::Array(Type::STRUCT), TakeExec<StructImpl>}, - // TODO: Reuse ListType kernel for MAP - {InputType::Array(Type::MAP), TakeExec<ListImpl<MapType>>}, - }; - - VectorKernel take_base; - take_base.init = TakeState::Init; - take_base.can_execute_chunkwise = false; - RegisterSelectionFunction( + {InputType::Array(Type::STRUCT), TakeExec<StructImpl>}, + // TODO: Reuse ListType kernel for MAP + {InputType::Array(Type::MAP), TakeExec<ListImpl<MapType>>}, + }; + + VectorKernel take_base; + take_base.init = TakeState::Init; + take_base.can_execute_chunkwise = false; + RegisterSelectionFunction( "array_take", &array_take_doc, take_base, - /*selection_type=*/InputType(match::Integer(), ValueDescr::ARRAY), - take_kernel_descrs, &kDefaultTakeOptions, registry); - - DCHECK_OK(registry->AddFunction(std::make_shared<TakeMetaFunction>())); -} - -} // namespace internal -} // namespace compute -} // namespace arrow + /*selection_type=*/InputType(match::Integer(), ValueDescr::ARRAY), + take_kernel_descrs, &kDefaultTakeOptions, registry); + + DCHECK_OK(registry->AddFunction(std::make_shared<TakeMetaFunction>())); +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_sort.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_sort.cc index b7e7adc70e..7fa43e715d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_sort.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/kernels/vector_sort.cc @@ -1,30 +1,30 @@ -// 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. - -#include <algorithm> +// 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. + +#include <algorithm> #include <cmath> -#include <limits> -#include <numeric> +#include <limits> +#include <numeric> #include <type_traits> #include <utility> - -#include "arrow/array/data.h" -#include "arrow/compute/api_vector.h" -#include "arrow/compute/kernels/common.h" + +#include "arrow/array/data.h" +#include "arrow/compute/api_vector.h" +#include "arrow/compute/kernels/common.h" #include "arrow/compute/kernels/util_internal.h" #include "arrow/table.h" #include "arrow/type_traits.h" @@ -32,16 +32,16 @@ #include "arrow/util/bitmap.h" #include "arrow/util/bitmap_ops.h" #include "arrow/util/checked_cast.h" -#include "arrow/util/optional.h" +#include "arrow/util/optional.h" #include "arrow/visitor_inline.h" - -namespace arrow { + +namespace arrow { using internal::checked_cast; -namespace compute { +namespace compute { namespace internal { - + // Visit all physical types for which sorting is implemented. #define VISIT_PHYSICAL_TYPES(VISIT) \ VISIT(BooleanType) \ @@ -61,8 +61,8 @@ namespace internal { VISIT(Decimal128Type) \ VISIT(Decimal256Type) -namespace { - +namespace { + // The target chunk in a chunked array. template <typename ArrayType> struct ResolvedChunk { @@ -315,59 +315,59 @@ uint64_t* PartitionNulls(uint64_t* indices_begin, uint64_t* indices_end, null_count); } -// ---------------------------------------------------------------------- -// partition_nth_indices implementation - -// We need to preserve the options -using PartitionNthToIndicesState = internal::OptionsWrapper<PartitionNthOptions>; - -template <typename OutType, typename InType> -struct PartitionNthToIndices { - using ArrayType = typename TypeTraits<InType>::ArrayType; +// ---------------------------------------------------------------------- +// partition_nth_indices implementation + +// We need to preserve the options +using PartitionNthToIndicesState = internal::OptionsWrapper<PartitionNthOptions>; + +template <typename OutType, typename InType> +struct PartitionNthToIndices { + using ArrayType = typename TypeTraits<InType>::ArrayType; static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { using GetView = GetViewType<InType>; - if (ctx->state() == nullptr) { + if (ctx->state() == nullptr) { return Status::Invalid("NthToIndices requires PartitionNthOptions"); - } - + } + ArrayType arr(batch[0].array()); - - int64_t pivot = PartitionNthToIndicesState::Get(ctx).pivot; - if (pivot > arr.length()) { + + int64_t pivot = PartitionNthToIndicesState::Get(ctx).pivot; + if (pivot > arr.length()) { return Status::IndexError("NthToIndices index out of bound"); - } - ArrayData* out_arr = out->mutable_array(); - uint64_t* out_begin = out_arr->GetMutableValues<uint64_t>(1); - uint64_t* out_end = out_begin + arr.length(); - std::iota(out_begin, out_end, 0); - if (pivot == arr.length()) { + } + ArrayData* out_arr = out->mutable_array(); + uint64_t* out_begin = out_arr->GetMutableValues<uint64_t>(1); + uint64_t* out_end = out_begin + arr.length(); + std::iota(out_begin, out_end, 0); + if (pivot == arr.length()) { return Status::OK(); - } + } auto nulls_begin = PartitionNulls<ArrayType, NonStablePartitioner>(out_begin, out_end, arr, 0); - auto nth_begin = out_begin + pivot; - if (nth_begin < nulls_begin) { - std::nth_element(out_begin, nth_begin, nulls_begin, - [&arr](uint64_t left, uint64_t right) { + auto nth_begin = out_begin + pivot; + if (nth_begin < nulls_begin) { + std::nth_element(out_begin, nth_begin, nulls_begin, + [&arr](uint64_t left, uint64_t right) { const auto lval = GetView::LogicalValue(arr.GetView(left)); const auto rval = GetView::LogicalValue(arr.GetView(right)); return lval < rval; - }); - } + }); + } return Status::OK(); - } -}; - + } +}; + // ---------------------------------------------------------------------- // Array sorting implementations -template <typename ArrayType, typename VisitorNotNull, typename VisitorNull> -inline void VisitRawValuesInline(const ArrayType& values, - VisitorNotNull&& visitor_not_null, - VisitorNull&& visitor_null) { - const auto data = values.raw_values(); +template <typename ArrayType, typename VisitorNotNull, typename VisitorNull> +inline void VisitRawValuesInline(const ArrayType& values, + VisitorNotNull&& visitor_not_null, + VisitorNull&& visitor_null) { + const auto data = values.raw_values(); VisitBitBlocksVoid( values.null_bitmap(), values.offset(), values.length(), [&](int64_t i) { visitor_not_null(data[i]); }, [&]() { visitor_null(); }); @@ -383,20 +383,20 @@ inline void VisitRawValuesInline(const BooleanArray& values, values.null_bitmap(), values.offset(), values.length(), [&](int64_t i) { visitor_not_null(BitUtil::GetBit(data, values.offset() + i)); }, [&]() { visitor_null(); }); - } else { + } else { // Can avoid GetBit() overhead in the no-nulls case VisitBitBlocksVoid( values.data()->buffers[1], values.offset(), values.length(), [&](int64_t i) { visitor_not_null(true); }, [&]() { visitor_not_null(false); }); - } -} - -template <typename ArrowType> + } +} + +template <typename ArrowType> class ArrayCompareSorter { - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; using GetView = GetViewType<ArrowType>; - - public: + + public: // Returns where null starts. // // `offset` is used when this is called on a chunk of a chunked array @@ -420,54 +420,54 @@ class ArrayCompareSorter { // If we use 'right < left' here, '<' is only required. return rhs < lhs; }); - } + } return nulls_begin; - } -}; - -template <typename ArrowType> + } +}; + +template <typename ArrowType> class ArrayCountSorter { - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; - using c_type = typename ArrowType::c_type; - - public: + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; + using c_type = typename ArrowType::c_type; + + public: ArrayCountSorter() = default; - + explicit ArrayCountSorter(c_type min, c_type max) { SetMinMax(min, max); } - - // Assume: max >= min && (max - min) < 4Gi - void SetMinMax(c_type min, c_type max) { - min_ = min; - value_range_ = static_cast<uint32_t>(max - min) + 1; - } - + + // Assume: max >= min && (max - min) < 4Gi + void SetMinMax(c_type min, c_type max) { + min_ = min; + value_range_ = static_cast<uint32_t>(max - min) + 1; + } + // Returns where null starts. uint64_t* Sort(uint64_t* indices_begin, uint64_t* indices_end, const ArrayType& values, int64_t offset, const ArraySortOptions& options) { - // 32bit counter performs much better than 64bit one - if (values.length() < (1LL << 32)) { + // 32bit counter performs much better than 64bit one + if (values.length() < (1LL << 32)) { return SortInternal<uint32_t>(indices_begin, indices_end, values, offset, options); - } else { + } else { return SortInternal<uint64_t>(indices_begin, indices_end, values, offset, options); - } - } - - private: - c_type min_{0}; - uint32_t value_range_{0}; - + } + } + + private: + c_type min_{0}; + uint32_t value_range_{0}; + // Returns where null starts. // // `offset` is used when this is called on a chunk of a chunked array - template <typename CounterType> + template <typename CounterType> uint64_t* SortInternal(uint64_t* indices_begin, uint64_t* indices_end, const ArrayType& values, int64_t offset, const ArraySortOptions& options) { - const uint32_t value_range = value_range_; - - // first slot reserved for prefix sum - std::vector<CounterType> counts(1 + value_range); - + const uint32_t value_range = value_range_; + + // first slot reserved for prefix sum + std::vector<CounterType> counts(1 + value_range); + if (options.order == SortOrder::Ascending) { VisitRawValuesInline( values, [&](c_type v) { ++counts[v - min_ + 1]; }, []() {}); @@ -497,7 +497,7 @@ class ArrayCountSorter { } } }; - + using ::arrow::internal::Bitmap; template <> @@ -526,135 +526,135 @@ class ArrayCountSorter<BooleanType> { } else { // zeros start after ones counts[0] = ones; - } - VisitRawValuesInline( + } + VisitRawValuesInline( values, [&](bool v) { indices_begin[counts[v]++] = index++; }, [&]() { indices_begin[null_position++] = index++; }); return nulls_begin; - } -}; - -// Sort integers with counting sort or comparison based sorting algorithm -// - Use O(n) counting sort if values are in a small range -// - Use O(nlogn) std::stable_sort otherwise -template <typename ArrowType> + } +}; + +// Sort integers with counting sort or comparison based sorting algorithm +// - Use O(n) counting sort if values are in a small range +// - Use O(nlogn) std::stable_sort otherwise +template <typename ArrowType> class ArrayCountOrCompareSorter { - using ArrayType = typename TypeTraits<ArrowType>::ArrayType; - using c_type = typename ArrowType::c_type; - - public: + using ArrayType = typename TypeTraits<ArrowType>::ArrayType; + using c_type = typename ArrowType::c_type; + + public: // Returns where null starts. // // `offset` is used when this is called on a chunk of a chunked array uint64_t* Sort(uint64_t* indices_begin, uint64_t* indices_end, const ArrayType& values, int64_t offset, const ArraySortOptions& options) { - if (values.length() >= countsort_min_len_ && values.length() > values.null_count()) { + if (values.length() >= countsort_min_len_ && values.length() > values.null_count()) { c_type min, max; std::tie(min, max) = GetMinMax<c_type>(*values.data()); - - // For signed int32/64, (max - min) may overflow and trigger UBSAN. - // Cast to largest unsigned type(uint64_t) before subtraction. - if (static_cast<uint64_t>(max) - static_cast<uint64_t>(min) <= - countsort_max_range_) { - count_sorter_.SetMinMax(min, max); + + // For signed int32/64, (max - min) may overflow and trigger UBSAN. + // Cast to largest unsigned type(uint64_t) before subtraction. + if (static_cast<uint64_t>(max) - static_cast<uint64_t>(min) <= + countsort_max_range_) { + count_sorter_.SetMinMax(min, max); return count_sorter_.Sort(indices_begin, indices_end, values, offset, options); - } - } - + } + } + return compare_sorter_.Sort(indices_begin, indices_end, values, offset, options); - } - - private: + } + + private: ArrayCompareSorter<ArrowType> compare_sorter_; ArrayCountSorter<ArrowType> count_sorter_; - - // Cross point to prefer counting sort than stl::stable_sort(merge sort) - // - array to be sorted is longer than "count_min_len_" - // - value range (max-min) is within "count_max_range_" - // - // The optimal setting depends heavily on running CPU. Below setting is - // conservative to adapt to various hardware and keep code simple. - // It's possible to decrease array-len and/or increase value-range to cover - // more cases, or setup a table for best array-len/value-range combinations. - // See https://issues.apache.org/jira/browse/ARROW-1571 for detailed analysis. - static const uint32_t countsort_min_len_ = 1024; - static const uint32_t countsort_max_range_ = 4096; -}; - -template <typename Type, typename Enable = void> + + // Cross point to prefer counting sort than stl::stable_sort(merge sort) + // - array to be sorted is longer than "count_min_len_" + // - value range (max-min) is within "count_max_range_" + // + // The optimal setting depends heavily on running CPU. Below setting is + // conservative to adapt to various hardware and keep code simple. + // It's possible to decrease array-len and/or increase value-range to cover + // more cases, or setup a table for best array-len/value-range combinations. + // See https://issues.apache.org/jira/browse/ARROW-1571 for detailed analysis. + static const uint32_t countsort_min_len_ = 1024; + static const uint32_t countsort_max_range_ = 4096; +}; + +template <typename Type, typename Enable = void> struct ArraySorter; - -template <> + +template <> struct ArraySorter<BooleanType> { ArrayCountSorter<BooleanType> impl; -}; - -template <> +}; + +template <> struct ArraySorter<UInt8Type> { ArrayCountSorter<UInt8Type> impl; ArraySorter() : impl(0, 255) {} -}; - +}; + template <> struct ArraySorter<Int8Type> { ArrayCountSorter<Int8Type> impl; ArraySorter() : impl(-128, 127) {} }; -template <typename Type> +template <typename Type> struct ArraySorter<Type, enable_if_t<(is_integer_type<Type>::value && (sizeof(typename Type::c_type) > 1)) || is_temporal_type<Type>::value>> { ArrayCountOrCompareSorter<Type> impl; -}; - -template <typename Type> +}; + +template <typename Type> struct ArraySorter< Type, enable_if_t<is_floating_type<Type>::value || is_base_binary_type<Type>::value || is_fixed_size_binary_type<Type>::value>> { ArrayCompareSorter<Type> impl; -}; - +}; + using ArraySortIndicesState = internal::OptionsWrapper<ArraySortOptions>; -template <typename OutType, typename InType> +template <typename OutType, typename InType> struct ArraySortIndices { - using ArrayType = typename TypeTraits<InType>::ArrayType; + using ArrayType = typename TypeTraits<InType>::ArrayType; static Status Exec(KernelContext* ctx, const ExecBatch& batch, Datum* out) { const auto& options = ArraySortIndicesState::Get(ctx); ArrayType arr(batch[0].array()); - ArrayData* out_arr = out->mutable_array(); - uint64_t* out_begin = out_arr->GetMutableValues<uint64_t>(1); - uint64_t* out_end = out_begin + arr.length(); + ArrayData* out_arr = out->mutable_array(); + uint64_t* out_begin = out_arr->GetMutableValues<uint64_t>(1); + uint64_t* out_end = out_begin + arr.length(); std::iota(out_begin, out_end, 0); - + ArraySorter<InType> sorter; sorter.impl.Sort(out_begin, out_end, arr, 0, options); return Status::OK(); - } -}; - -// Sort indices kernels implemented for -// + } +}; + +// Sort indices kernels implemented for +// // * Boolean type -// * Number types -// * Base binary types - -template <template <typename...> class ExecTemplate> -void AddSortingKernels(VectorKernel base, VectorFunction* func) { +// * Number types +// * Base binary types + +template <template <typename...> class ExecTemplate> +void AddSortingKernels(VectorKernel base, VectorFunction* func) { // bool type base.signature = KernelSignature::Make({InputType::Array(boolean())}, uint64()); base.exec = ExecTemplate<UInt64Type, BooleanType>::Exec; DCHECK_OK(func->AddKernel(base)); - for (const auto& ty : NumericTypes()) { + for (const auto& ty : NumericTypes()) { auto physical_type = GetPhysicalType(ty); - base.signature = KernelSignature::Make({InputType::Array(ty)}, uint64()); + base.signature = KernelSignature::Make({InputType::Array(ty)}, uint64()); base.exec = GenerateNumeric<ExecTemplate, UInt64Type>(*physical_type); - DCHECK_OK(func->AddKernel(base)); - } + DCHECK_OK(func->AddKernel(base)); + } for (const auto& ty : TemporalTypes()) { auto physical_type = GetPhysicalType(ty); base.signature = KernelSignature::Make({InputType::Array(ty)}, uint64()); @@ -666,18 +666,18 @@ void AddSortingKernels(VectorKernel base, VectorFunction* func) { base.exec = GenerateDecimal<ExecTemplate, UInt64Type>(id); DCHECK_OK(func->AddKernel(base)); } - for (const auto& ty : BaseBinaryTypes()) { + for (const auto& ty : BaseBinaryTypes()) { auto physical_type = GetPhysicalType(ty); - base.signature = KernelSignature::Make({InputType::Array(ty)}, uint64()); + base.signature = KernelSignature::Make({InputType::Array(ty)}, uint64()); base.exec = GenerateVarBinaryBase<ExecTemplate, UInt64Type>(*physical_type); - DCHECK_OK(func->AddKernel(base)); - } + DCHECK_OK(func->AddKernel(base)); + } base.signature = KernelSignature::Make({InputType::Array(Type::FIXED_SIZE_BINARY)}, uint64()); base.exec = ExecTemplate<UInt64Type, FixedSizeBinaryType>::Exec; DCHECK_OK(func->AddKernel(base)); -} - +} + // ---------------------------------------------------------------------- // ChunkedArray sorting implementations @@ -1808,31 +1808,31 @@ const FunctionDoc partition_nth_indices_doc( } // namespace -void RegisterVectorSort(FunctionRegistry* registry) { - // The kernel outputs into preallocated memory and is never null - VectorKernel base; - base.mem_allocation = MemAllocation::PREALLOCATE; - base.null_handling = NullHandling::OUTPUT_NOT_NULL; - +void RegisterVectorSort(FunctionRegistry* registry) { + // The kernel outputs into preallocated memory and is never null + VectorKernel base; + base.mem_allocation = MemAllocation::PREALLOCATE; + base.null_handling = NullHandling::OUTPUT_NOT_NULL; + auto array_sort_indices = std::make_shared<VectorFunction>( "array_sort_indices", Arity::Unary(), &array_sort_indices_doc, &kDefaultArraySortOptions); base.init = ArraySortIndicesState::Init; AddSortingKernels<ArraySortIndices>(base, array_sort_indices.get()); DCHECK_OK(registry->AddFunction(std::move(array_sort_indices))); - + DCHECK_OK(registry->AddFunction(std::make_shared<SortIndicesMetaFunction>())); - // partition_nth_indices has a parameter so needs its init function + // partition_nth_indices has a parameter so needs its init function auto part_indices = std::make_shared<VectorFunction>( "partition_nth_indices", Arity::Unary(), &partition_nth_indices_doc); - base.init = PartitionNthToIndicesState::Init; - AddSortingKernels<PartitionNthToIndices>(base, part_indices.get()); - DCHECK_OK(registry->AddFunction(std::move(part_indices))); -} - + base.init = PartitionNthToIndicesState::Init; + AddSortingKernels<PartitionNthToIndices>(base, part_indices.get()); + DCHECK_OK(registry->AddFunction(std::move(part_indices))); +} + #undef VISIT_PHYSICAL_TYPES -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.cc b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.cc index 7439faa7b2..ca7b613730 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.cc @@ -1,64 +1,64 @@ -// 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. - -#include "arrow/compute/registry.h" - -#include <algorithm> -#include <memory> -#include <mutex> -#include <unordered_map> -#include <utility> - -#include "arrow/compute/function.h" +// 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. + +#include "arrow/compute/registry.h" + +#include <algorithm> +#include <memory> +#include <mutex> +#include <unordered_map> +#include <utility> + +#include "arrow/compute/function.h" #include "arrow/compute/function_internal.h" -#include "arrow/compute/registry_internal.h" -#include "arrow/status.h" +#include "arrow/compute/registry_internal.h" +#include "arrow/status.h" #include "arrow/util/logging.h" - -namespace arrow { -namespace compute { - -class FunctionRegistry::FunctionRegistryImpl { - public: - Status AddFunction(std::shared_ptr<Function> function, bool allow_overwrite) { + +namespace arrow { +namespace compute { + +class FunctionRegistry::FunctionRegistryImpl { + public: + Status AddFunction(std::shared_ptr<Function> function, bool allow_overwrite) { RETURN_NOT_OK(function->Validate()); - std::lock_guard<std::mutex> mutation_guard(lock_); - - const std::string& name = function->name(); - auto it = name_to_function_.find(name); - if (it != name_to_function_.end() && !allow_overwrite) { - return Status::KeyError("Already have a function registered with name: ", name); - } - name_to_function_[name] = std::move(function); - return Status::OK(); - } - - Status AddAlias(const std::string& target_name, const std::string& source_name) { - std::lock_guard<std::mutex> mutation_guard(lock_); - - auto it = name_to_function_.find(source_name); - if (it == name_to_function_.end()) { - return Status::KeyError("No function registered with name: ", source_name); - } - name_to_function_[target_name] = it->second; - return Status::OK(); - } - + std::lock_guard<std::mutex> mutation_guard(lock_); + + const std::string& name = function->name(); + auto it = name_to_function_.find(name); + if (it != name_to_function_.end() && !allow_overwrite) { + return Status::KeyError("Already have a function registered with name: ", name); + } + name_to_function_[name] = std::move(function); + return Status::OK(); + } + + Status AddAlias(const std::string& target_name, const std::string& source_name) { + std::lock_guard<std::mutex> mutation_guard(lock_); + + auto it = name_to_function_.find(source_name); + if (it == name_to_function_.end()) { + return Status::KeyError("No function registered with name: ", source_name); + } + name_to_function_[target_name] = it->second; + return Status::OK(); + } + Status AddFunctionOptionsType(const FunctionOptionsType* options_type, bool allow_overwrite = false) { std::lock_guard<std::mutex> mutation_guard(lock_); @@ -73,23 +73,23 @@ class FunctionRegistry::FunctionRegistryImpl { return Status::OK(); } - Result<std::shared_ptr<Function>> GetFunction(const std::string& name) const { - auto it = name_to_function_.find(name); - if (it == name_to_function_.end()) { - return Status::KeyError("No function registered with name: ", name); - } - return it->second; - } - - std::vector<std::string> GetFunctionNames() const { - std::vector<std::string> results; - for (auto it : name_to_function_) { - results.push_back(it.first); - } - std::sort(results.begin(), results.end()); - return results; - } - + Result<std::shared_ptr<Function>> GetFunction(const std::string& name) const { + auto it = name_to_function_.find(name); + if (it == name_to_function_.end()) { + return Status::KeyError("No function registered with name: ", name); + } + return it->second; + } + + std::vector<std::string> GetFunctionNames() const { + std::vector<std::string> results; + for (auto it : name_to_function_) { + results.push_back(it.first); + } + std::sort(results.begin(), results.end()); + return results; + } + Result<const FunctionOptionsType*> GetFunctionOptionsType( const std::string& name) const { auto it = name_to_options_type_.find(name); @@ -99,80 +99,80 @@ class FunctionRegistry::FunctionRegistryImpl { return it->second; } - int num_functions() const { return static_cast<int>(name_to_function_.size()); } - - private: - std::mutex lock_; - std::unordered_map<std::string, std::shared_ptr<Function>> name_to_function_; + int num_functions() const { return static_cast<int>(name_to_function_.size()); } + + private: + std::mutex lock_; + std::unordered_map<std::string, std::shared_ptr<Function>> name_to_function_; std::unordered_map<std::string, const FunctionOptionsType*> name_to_options_type_; -}; - -std::unique_ptr<FunctionRegistry> FunctionRegistry::Make() { - return std::unique_ptr<FunctionRegistry>(new FunctionRegistry()); -} - -FunctionRegistry::FunctionRegistry() { impl_.reset(new FunctionRegistryImpl()); } - -FunctionRegistry::~FunctionRegistry() {} - -Status FunctionRegistry::AddFunction(std::shared_ptr<Function> function, - bool allow_overwrite) { - return impl_->AddFunction(std::move(function), allow_overwrite); -} - -Status FunctionRegistry::AddAlias(const std::string& target_name, - const std::string& source_name) { - return impl_->AddAlias(target_name, source_name); -} - +}; + +std::unique_ptr<FunctionRegistry> FunctionRegistry::Make() { + return std::unique_ptr<FunctionRegistry>(new FunctionRegistry()); +} + +FunctionRegistry::FunctionRegistry() { impl_.reset(new FunctionRegistryImpl()); } + +FunctionRegistry::~FunctionRegistry() {} + +Status FunctionRegistry::AddFunction(std::shared_ptr<Function> function, + bool allow_overwrite) { + return impl_->AddFunction(std::move(function), allow_overwrite); +} + +Status FunctionRegistry::AddAlias(const std::string& target_name, + const std::string& source_name) { + return impl_->AddAlias(target_name, source_name); +} + Status FunctionRegistry::AddFunctionOptionsType(const FunctionOptionsType* options_type, bool allow_overwrite) { return impl_->AddFunctionOptionsType(options_type, allow_overwrite); } -Result<std::shared_ptr<Function>> FunctionRegistry::GetFunction( - const std::string& name) const { - return impl_->GetFunction(name); -} - -std::vector<std::string> FunctionRegistry::GetFunctionNames() const { - return impl_->GetFunctionNames(); -} - +Result<std::shared_ptr<Function>> FunctionRegistry::GetFunction( + const std::string& name) const { + return impl_->GetFunction(name); +} + +std::vector<std::string> FunctionRegistry::GetFunctionNames() const { + return impl_->GetFunctionNames(); +} + Result<const FunctionOptionsType*> FunctionRegistry::GetFunctionOptionsType( const std::string& name) const { return impl_->GetFunctionOptionsType(name); } -int FunctionRegistry::num_functions() const { return impl_->num_functions(); } - -namespace internal { - -static std::unique_ptr<FunctionRegistry> CreateBuiltInRegistry() { - auto registry = FunctionRegistry::Make(); - - // Scalar functions - RegisterScalarArithmetic(registry.get()); - RegisterScalarBoolean(registry.get()); - RegisterScalarCast(registry.get()); - RegisterScalarComparison(registry.get()); - RegisterScalarNested(registry.get()); - RegisterScalarSetLookup(registry.get()); - RegisterScalarStringAscii(registry.get()); - RegisterScalarValidity(registry.get()); - RegisterScalarFillNull(registry.get()); +int FunctionRegistry::num_functions() const { return impl_->num_functions(); } + +namespace internal { + +static std::unique_ptr<FunctionRegistry> CreateBuiltInRegistry() { + auto registry = FunctionRegistry::Make(); + + // Scalar functions + RegisterScalarArithmetic(registry.get()); + RegisterScalarBoolean(registry.get()); + RegisterScalarCast(registry.get()); + RegisterScalarComparison(registry.get()); + RegisterScalarNested(registry.get()); + RegisterScalarSetLookup(registry.get()); + RegisterScalarStringAscii(registry.get()); + RegisterScalarValidity(registry.get()); + RegisterScalarFillNull(registry.get()); RegisterScalarIfElse(registry.get()); RegisterScalarTemporal(registry.get()); - + RegisterScalarOptions(registry.get()); - - // Vector functions - RegisterVectorHash(registry.get()); + + // Vector functions + RegisterVectorHash(registry.get()); RegisterVectorReplace(registry.get()); - RegisterVectorSelection(registry.get()); - RegisterVectorNested(registry.get()); - RegisterVectorSort(registry.get()); - + RegisterVectorSelection(registry.get()); + RegisterVectorNested(registry.get()); + RegisterVectorSort(registry.get()); + RegisterVectorOptions(registry.get()); // Aggregate functions @@ -185,15 +185,15 @@ static std::unique_ptr<FunctionRegistry> CreateBuiltInRegistry() { RegisterAggregateOptions(registry.get()); - return registry; -} - -} // namespace internal - -FunctionRegistry* GetFunctionRegistry() { - static auto g_registry = internal::CreateBuiltInRegistry(); - return g_registry.get(); -} - -} // namespace compute -} // namespace arrow + return registry; +} + +} // namespace internal + +FunctionRegistry* GetFunctionRegistry() { + static auto g_registry = internal::CreateBuiltInRegistry(); + return g_registry.get(); +} + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.h index 6769ecf79c..e83036db6a 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry.h @@ -1,93 +1,93 @@ -// 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. - -// NOTE: API is EXPERIMENTAL and will change without going through a -// deprecation cycle - -#pragma once - -#include <memory> -#include <string> -#include <vector> - -#include "arrow/result.h" -#include "arrow/status.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace compute { - -class Function; +// 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. + +// NOTE: API is EXPERIMENTAL and will change without going through a +// deprecation cycle + +#pragma once + +#include <memory> +#include <string> +#include <vector> + +#include "arrow/result.h" +#include "arrow/status.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace compute { + +class Function; class FunctionOptionsType; - -/// \brief A mutable central function registry for built-in functions as well -/// as user-defined functions. Functions are implementations of -/// arrow::compute::Function. -/// -/// Generally, each function contains kernels which are implementations of a -/// function for a specific argument signature. After looking up a function in -/// the registry, one can either execute it eagerly with Function::Execute or -/// use one of the function's dispatch methods to pick a suitable kernel for -/// lower-level function execution. -class ARROW_EXPORT FunctionRegistry { - public: - ~FunctionRegistry(); - - /// \brief Construct a new registry. Most users only need to use the global - /// registry - static std::unique_ptr<FunctionRegistry> Make(); - - /// \brief Add a new function to the registry. Returns Status::KeyError if a - /// function with the same name is already registered - Status AddFunction(std::shared_ptr<Function> function, bool allow_overwrite = false); - - /// \brief Add aliases for the given function name. Returns Status::KeyError if the - /// function with the given name is not registered - Status AddAlias(const std::string& target_name, const std::string& source_name); - + +/// \brief A mutable central function registry for built-in functions as well +/// as user-defined functions. Functions are implementations of +/// arrow::compute::Function. +/// +/// Generally, each function contains kernels which are implementations of a +/// function for a specific argument signature. After looking up a function in +/// the registry, one can either execute it eagerly with Function::Execute or +/// use one of the function's dispatch methods to pick a suitable kernel for +/// lower-level function execution. +class ARROW_EXPORT FunctionRegistry { + public: + ~FunctionRegistry(); + + /// \brief Construct a new registry. Most users only need to use the global + /// registry + static std::unique_ptr<FunctionRegistry> Make(); + + /// \brief Add a new function to the registry. Returns Status::KeyError if a + /// function with the same name is already registered + Status AddFunction(std::shared_ptr<Function> function, bool allow_overwrite = false); + + /// \brief Add aliases for the given function name. Returns Status::KeyError if the + /// function with the given name is not registered + Status AddAlias(const std::string& target_name, const std::string& source_name); + /// \brief Add a new function options type to the registry. Returns Status::KeyError if /// a function options type with the same name is already registered Status AddFunctionOptionsType(const FunctionOptionsType* options_type, bool allow_overwrite = false); - /// \brief Retrieve a function by name from the registry - Result<std::shared_ptr<Function>> GetFunction(const std::string& name) const; - - /// \brief Return vector of all entry names in the registry. Helpful for - /// displaying a manifest of available functions - std::vector<std::string> GetFunctionNames() const; - + /// \brief Retrieve a function by name from the registry + Result<std::shared_ptr<Function>> GetFunction(const std::string& name) const; + + /// \brief Return vector of all entry names in the registry. Helpful for + /// displaying a manifest of available functions + std::vector<std::string> GetFunctionNames() const; + /// \brief Retrieve a function options type by name from the registry Result<const FunctionOptionsType*> GetFunctionOptionsType( const std::string& name) const; - /// \brief The number of currently registered functions - int num_functions() const; - - private: - FunctionRegistry(); - - // Use PIMPL pattern to not have std::unordered_map here - class FunctionRegistryImpl; - std::unique_ptr<FunctionRegistryImpl> impl_; -}; - -/// \brief Return the process-global function registry -ARROW_EXPORT FunctionRegistry* GetFunctionRegistry(); - -} // namespace compute -} // namespace arrow + /// \brief The number of currently registered functions + int num_functions() const; + + private: + FunctionRegistry(); + + // Use PIMPL pattern to not have std::unordered_map here + class FunctionRegistryImpl; + std::unique_ptr<FunctionRegistryImpl> impl_; +}; + +/// \brief Return the process-global function registry +ARROW_EXPORT FunctionRegistry* GetFunctionRegistry(); + +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry_internal.h index f078bc5510..892b54341d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/registry_internal.h @@ -1,63 +1,63 @@ -// 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 - -namespace arrow { -namespace compute { - -class FunctionRegistry; - -namespace internal { - -// Built-in scalar / elementwise functions -void RegisterScalarArithmetic(FunctionRegistry* registry); -void RegisterScalarBoolean(FunctionRegistry* registry); -void RegisterScalarCast(FunctionRegistry* registry); -void RegisterScalarComparison(FunctionRegistry* registry); -void RegisterScalarNested(FunctionRegistry* registry); -void RegisterScalarSetLookup(FunctionRegistry* registry); -void RegisterScalarStringAscii(FunctionRegistry* registry); -void RegisterScalarValidity(FunctionRegistry* registry); -void RegisterScalarFillNull(FunctionRegistry* registry); +// 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 + +namespace arrow { +namespace compute { + +class FunctionRegistry; + +namespace internal { + +// Built-in scalar / elementwise functions +void RegisterScalarArithmetic(FunctionRegistry* registry); +void RegisterScalarBoolean(FunctionRegistry* registry); +void RegisterScalarCast(FunctionRegistry* registry); +void RegisterScalarComparison(FunctionRegistry* registry); +void RegisterScalarNested(FunctionRegistry* registry); +void RegisterScalarSetLookup(FunctionRegistry* registry); +void RegisterScalarStringAscii(FunctionRegistry* registry); +void RegisterScalarValidity(FunctionRegistry* registry); +void RegisterScalarFillNull(FunctionRegistry* registry); void RegisterScalarIfElse(FunctionRegistry* registry); void RegisterScalarTemporal(FunctionRegistry* registry); - + void RegisterScalarOptions(FunctionRegistry* registry); -// Vector functions -void RegisterVectorHash(FunctionRegistry* registry); +// Vector functions +void RegisterVectorHash(FunctionRegistry* registry); void RegisterVectorReplace(FunctionRegistry* registry); -void RegisterVectorSelection(FunctionRegistry* registry); -void RegisterVectorNested(FunctionRegistry* registry); -void RegisterVectorSort(FunctionRegistry* registry); - +void RegisterVectorSelection(FunctionRegistry* registry); +void RegisterVectorNested(FunctionRegistry* registry); +void RegisterVectorSort(FunctionRegistry* registry); + void RegisterVectorOptions(FunctionRegistry* registry); -// Aggregate functions -void RegisterScalarAggregateBasic(FunctionRegistry* registry); +// Aggregate functions +void RegisterScalarAggregateBasic(FunctionRegistry* registry); void RegisterScalarAggregateMode(FunctionRegistry* registry); void RegisterScalarAggregateQuantile(FunctionRegistry* registry); void RegisterScalarAggregateTDigest(FunctionRegistry* registry); void RegisterScalarAggregateVariance(FunctionRegistry* registry); void RegisterHashAggregateBasic(FunctionRegistry* registry); - + void RegisterAggregateOptions(FunctionRegistry* registry); -} // namespace internal -} // namespace compute -} // namespace arrow +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/type_fwd.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/type_fwd.h index 167cda6a04..eebc8c1b67 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/type_fwd.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/type_fwd.h @@ -1,48 +1,48 @@ -// 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 - -namespace arrow { - -struct Datum; +// 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 + +namespace arrow { + +struct Datum; struct ValueDescr; - -namespace compute { - + +namespace compute { + class Function; class FunctionOptions; class CastOptions; struct ExecBatch; -class ExecContext; -class KernelContext; - -struct Kernel; -struct ScalarKernel; -struct ScalarAggregateKernel; -struct VectorKernel; - +class ExecContext; +class KernelContext; + +struct Kernel; +struct ScalarKernel; +struct ScalarAggregateKernel; +struct VectorKernel; + struct KernelState; class Expression; class ExecNode; class ExecPlan; -} // namespace compute -} // namespace arrow +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/compute/util_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/compute/util_internal.h index 4f7e43dae5..396c2ca2a0 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/compute/util_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/compute/util_internal.h @@ -1,32 +1,32 @@ -// 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 "arrow/buffer.h" - -namespace arrow { -namespace compute { -namespace internal { - -static inline void ZeroMemory(Buffer* buffer) { - std::memset(buffer->mutable_data(), 0, buffer->size()); -} - -} // namespace internal -} // namespace compute -} // namespace arrow +// 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 "arrow/buffer.h" + +namespace arrow { +namespace compute { +namespace internal { + +static inline void ZeroMemory(Buffer* buffer) { + std::memset(buffer->mutable_data(), 0, buffer->size()); +} + +} // namespace internal +} // namespace compute +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/api.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/api.h index ed247e369b..7bf3931576 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/api.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/api.h @@ -1,26 +1,26 @@ -// 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 "arrow/csv/options.h" -#include "arrow/csv/reader.h" - -// The writer depends on compute module for casting. -#ifdef ARROW_COMPUTE -#include "arrow/csv/writer.h" -#endif +// 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 "arrow/csv/options.h" +#include "arrow/csv/reader.h" + +// The writer depends on compute module for casting. +#ifdef ARROW_COMPUTE +#include "arrow/csv/writer.h" +#endif diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.cc index 900f2d6228..b3a0dead59 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.cc @@ -1,300 +1,300 @@ -// 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. - -#include "arrow/csv/chunker.h" - -#include <cstdint> -#include <memory> -#include <utility> - -#include "arrow/status.h" -#include "arrow/util/logging.h" -#include "arrow/util/make_unique.h" -#include "arrow/util/string_view.h" - -namespace arrow { -namespace csv { - -namespace { - -// NOTE: csvmonkey (https://github.com/dw/csvmonkey) has optimization ideas - -template <bool quoting, bool escaping> -class Lexer { - public: - enum State { - FIELD_START, - IN_FIELD, - AT_ESCAPE, - IN_QUOTED_FIELD, - AT_QUOTED_QUOTE, - AT_QUOTED_ESCAPE - }; - - explicit Lexer(const ParseOptions& options) : options_(options) { - DCHECK_EQ(quoting, options_.quoting); - DCHECK_EQ(escaping, options_.escaping); - } - - const char* ReadLine(const char* data, const char* data_end) { - // The parsing state machine - char c; - DCHECK_GT(data_end - data, 0); - if (ARROW_PREDICT_TRUE(state_ == FIELD_START)) { - goto FieldStart; - } - switch (state_) { - case FIELD_START: - goto FieldStart; - case IN_FIELD: - goto InField; - case AT_ESCAPE: - goto AtEscape; - case IN_QUOTED_FIELD: - goto InQuotedField; - case AT_QUOTED_QUOTE: - goto AtQuotedQuote; - case AT_QUOTED_ESCAPE: - goto AtQuotedEscape; - } - - FieldStart: - // At the start of a field - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = FIELD_START; - goto AbortLine; - } - // Quoting is only recognized at start of field - if (quoting && *data == options_.quote_char) { - data++; - goto InQuotedField; - } else { - goto InField; - } - - InField: - // Inside a non-quoted part of a field - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = IN_FIELD; - goto AbortLine; - } - c = *data++; - if (escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = AT_ESCAPE; - goto AbortLine; - } - data++; - goto InField; - } - if (ARROW_PREDICT_FALSE(c == '\r')) { - if (ARROW_PREDICT_TRUE(data != data_end) && *data == '\n') { - data++; - } - goto LineEnd; - } - if (ARROW_PREDICT_FALSE(c == '\n')) { - goto LineEnd; - } - if (ARROW_PREDICT_FALSE(c == options_.delimiter)) { - goto FieldEnd; - } - goto InField; - - AtEscape: - // Coming here if last block ended on a non-quoted escape - data++; - goto InField; - - InQuotedField: - // Inside a quoted part of a field - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = IN_QUOTED_FIELD; - goto AbortLine; - } - c = *data++; - if (escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = AT_QUOTED_ESCAPE; - goto AbortLine; - } - data++; - goto InQuotedField; - } - if (ARROW_PREDICT_FALSE(c == options_.quote_char)) { - if (ARROW_PREDICT_FALSE(data == data_end)) { - state_ = AT_QUOTED_QUOTE; - goto AbortLine; - } - if (options_.double_quote && *data == options_.quote_char) { - // Double-quoting - data++; - } else { - // End of single-quoting - goto InField; - } - } - goto InQuotedField; - - AtQuotedEscape: - // Coming here if last block ended on a quoted escape - data++; - goto InQuotedField; - - AtQuotedQuote: - // Coming here if last block ended on a quoted quote - if (options_.double_quote && *data == options_.quote_char) { - // Double-quoting - data++; - goto InQuotedField; - } else { - // End of single-quoting - goto InField; - } - - FieldEnd: - // At the end of a field - goto FieldStart; - - LineEnd: - state_ = FIELD_START; - return data; - - AbortLine: - // Truncated line - return nullptr; - } - - protected: - const ParseOptions& options_; - State state_ = FIELD_START; -}; - -// A BoundaryFinder implementation that assumes CSV cells can contain raw newlines, -// and uses actual CSV lexing to delimit them. -template <bool quoting, bool escaping> -class LexingBoundaryFinder : public BoundaryFinder { - public: - explicit LexingBoundaryFinder(ParseOptions options) : options_(std::move(options)) {} - - Status FindFirst(util::string_view partial, util::string_view block, - int64_t* out_pos) override { - Lexer<quoting, escaping> lexer(options_); - - const char* line_end = - lexer.ReadLine(partial.data(), partial.data() + partial.size()); - DCHECK_EQ(line_end, nullptr); // Otherwise `partial` is a whole CSV line - line_end = lexer.ReadLine(block.data(), block.data() + block.size()); - - if (line_end == nullptr) { - // No complete CSV line - *out_pos = -1; - } else { - *out_pos = static_cast<int64_t>(line_end - block.data()); - DCHECK_GT(*out_pos, 0); - } - return Status::OK(); - } - - Status FindLast(util::string_view block, int64_t* out_pos) override { - Lexer<quoting, escaping> lexer(options_); - - const char* data = block.data(); - const char* const data_end = block.data() + block.size(); - - while (data < data_end) { - const char* line_end = lexer.ReadLine(data, data_end); - if (line_end == nullptr) { - // Cannot read any further - break; - } - DCHECK_GT(line_end, data); - data = line_end; - } - if (data == block.data()) { - // No complete CSV line - *out_pos = -1; - } else { - *out_pos = static_cast<int64_t>(data - block.data()); - DCHECK_GT(*out_pos, 0); - } - return Status::OK(); - } - - Status FindNth(util::string_view partial, util::string_view block, int64_t count, - int64_t* out_pos, int64_t* num_found) override { - Lexer<quoting, escaping> lexer(options_); - int64_t found = 0; - const char* data = block.data(); - const char* const data_end = block.data() + block.size(); - - const char* line_end; - if (partial.size()) { - line_end = lexer.ReadLine(partial.data(), partial.data() + partial.size()); - DCHECK_EQ(line_end, nullptr); // Otherwise `partial` is a whole CSV line - } - - for (; data < data_end && found < count; ++found) { - line_end = lexer.ReadLine(data, data_end); - if (line_end == nullptr) { - // Cannot read any further - break; - } - DCHECK_GT(line_end, data); - data = line_end; - } - - if (data == block.data()) { - // No complete CSV line - *out_pos = kNoDelimiterFound; - } else { - *out_pos = static_cast<int64_t>(data - block.data()); - } - *num_found = found; - return Status::OK(); - } - - protected: - ParseOptions options_; -}; - -} // namespace - -std::unique_ptr<Chunker> MakeChunker(const ParseOptions& options) { - std::shared_ptr<BoundaryFinder> delimiter; - if (!options.newlines_in_values) { - delimiter = MakeNewlineBoundaryFinder(); - } else { - if (options.quoting) { - if (options.escaping) { - delimiter = std::make_shared<LexingBoundaryFinder<true, true>>(options); - } else { - delimiter = std::make_shared<LexingBoundaryFinder<true, false>>(options); - } - } else { - if (options.escaping) { - delimiter = std::make_shared<LexingBoundaryFinder<false, true>>(options); - } else { - delimiter = std::make_shared<LexingBoundaryFinder<false, false>>(options); - } - } - } - return internal::make_unique<Chunker>(std::move(delimiter)); -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/chunker.h" + +#include <cstdint> +#include <memory> +#include <utility> + +#include "arrow/status.h" +#include "arrow/util/logging.h" +#include "arrow/util/make_unique.h" +#include "arrow/util/string_view.h" + +namespace arrow { +namespace csv { + +namespace { + +// NOTE: csvmonkey (https://github.com/dw/csvmonkey) has optimization ideas + +template <bool quoting, bool escaping> +class Lexer { + public: + enum State { + FIELD_START, + IN_FIELD, + AT_ESCAPE, + IN_QUOTED_FIELD, + AT_QUOTED_QUOTE, + AT_QUOTED_ESCAPE + }; + + explicit Lexer(const ParseOptions& options) : options_(options) { + DCHECK_EQ(quoting, options_.quoting); + DCHECK_EQ(escaping, options_.escaping); + } + + const char* ReadLine(const char* data, const char* data_end) { + // The parsing state machine + char c; + DCHECK_GT(data_end - data, 0); + if (ARROW_PREDICT_TRUE(state_ == FIELD_START)) { + goto FieldStart; + } + switch (state_) { + case FIELD_START: + goto FieldStart; + case IN_FIELD: + goto InField; + case AT_ESCAPE: + goto AtEscape; + case IN_QUOTED_FIELD: + goto InQuotedField; + case AT_QUOTED_QUOTE: + goto AtQuotedQuote; + case AT_QUOTED_ESCAPE: + goto AtQuotedEscape; + } + + FieldStart: + // At the start of a field + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = FIELD_START; + goto AbortLine; + } + // Quoting is only recognized at start of field + if (quoting && *data == options_.quote_char) { + data++; + goto InQuotedField; + } else { + goto InField; + } + + InField: + // Inside a non-quoted part of a field + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = IN_FIELD; + goto AbortLine; + } + c = *data++; + if (escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = AT_ESCAPE; + goto AbortLine; + } + data++; + goto InField; + } + if (ARROW_PREDICT_FALSE(c == '\r')) { + if (ARROW_PREDICT_TRUE(data != data_end) && *data == '\n') { + data++; + } + goto LineEnd; + } + if (ARROW_PREDICT_FALSE(c == '\n')) { + goto LineEnd; + } + if (ARROW_PREDICT_FALSE(c == options_.delimiter)) { + goto FieldEnd; + } + goto InField; + + AtEscape: + // Coming here if last block ended on a non-quoted escape + data++; + goto InField; + + InQuotedField: + // Inside a quoted part of a field + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = IN_QUOTED_FIELD; + goto AbortLine; + } + c = *data++; + if (escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = AT_QUOTED_ESCAPE; + goto AbortLine; + } + data++; + goto InQuotedField; + } + if (ARROW_PREDICT_FALSE(c == options_.quote_char)) { + if (ARROW_PREDICT_FALSE(data == data_end)) { + state_ = AT_QUOTED_QUOTE; + goto AbortLine; + } + if (options_.double_quote && *data == options_.quote_char) { + // Double-quoting + data++; + } else { + // End of single-quoting + goto InField; + } + } + goto InQuotedField; + + AtQuotedEscape: + // Coming here if last block ended on a quoted escape + data++; + goto InQuotedField; + + AtQuotedQuote: + // Coming here if last block ended on a quoted quote + if (options_.double_quote && *data == options_.quote_char) { + // Double-quoting + data++; + goto InQuotedField; + } else { + // End of single-quoting + goto InField; + } + + FieldEnd: + // At the end of a field + goto FieldStart; + + LineEnd: + state_ = FIELD_START; + return data; + + AbortLine: + // Truncated line + return nullptr; + } + + protected: + const ParseOptions& options_; + State state_ = FIELD_START; +}; + +// A BoundaryFinder implementation that assumes CSV cells can contain raw newlines, +// and uses actual CSV lexing to delimit them. +template <bool quoting, bool escaping> +class LexingBoundaryFinder : public BoundaryFinder { + public: + explicit LexingBoundaryFinder(ParseOptions options) : options_(std::move(options)) {} + + Status FindFirst(util::string_view partial, util::string_view block, + int64_t* out_pos) override { + Lexer<quoting, escaping> lexer(options_); + + const char* line_end = + lexer.ReadLine(partial.data(), partial.data() + partial.size()); + DCHECK_EQ(line_end, nullptr); // Otherwise `partial` is a whole CSV line + line_end = lexer.ReadLine(block.data(), block.data() + block.size()); + + if (line_end == nullptr) { + // No complete CSV line + *out_pos = -1; + } else { + *out_pos = static_cast<int64_t>(line_end - block.data()); + DCHECK_GT(*out_pos, 0); + } + return Status::OK(); + } + + Status FindLast(util::string_view block, int64_t* out_pos) override { + Lexer<quoting, escaping> lexer(options_); + + const char* data = block.data(); + const char* const data_end = block.data() + block.size(); + + while (data < data_end) { + const char* line_end = lexer.ReadLine(data, data_end); + if (line_end == nullptr) { + // Cannot read any further + break; + } + DCHECK_GT(line_end, data); + data = line_end; + } + if (data == block.data()) { + // No complete CSV line + *out_pos = -1; + } else { + *out_pos = static_cast<int64_t>(data - block.data()); + DCHECK_GT(*out_pos, 0); + } + return Status::OK(); + } + + Status FindNth(util::string_view partial, util::string_view block, int64_t count, + int64_t* out_pos, int64_t* num_found) override { + Lexer<quoting, escaping> lexer(options_); + int64_t found = 0; + const char* data = block.data(); + const char* const data_end = block.data() + block.size(); + + const char* line_end; + if (partial.size()) { + line_end = lexer.ReadLine(partial.data(), partial.data() + partial.size()); + DCHECK_EQ(line_end, nullptr); // Otherwise `partial` is a whole CSV line + } + + for (; data < data_end && found < count; ++found) { + line_end = lexer.ReadLine(data, data_end); + if (line_end == nullptr) { + // Cannot read any further + break; + } + DCHECK_GT(line_end, data); + data = line_end; + } + + if (data == block.data()) { + // No complete CSV line + *out_pos = kNoDelimiterFound; + } else { + *out_pos = static_cast<int64_t>(data - block.data()); + } + *num_found = found; + return Status::OK(); + } + + protected: + ParseOptions options_; +}; + +} // namespace + +std::unique_ptr<Chunker> MakeChunker(const ParseOptions& options) { + std::shared_ptr<BoundaryFinder> delimiter; + if (!options.newlines_in_values) { + delimiter = MakeNewlineBoundaryFinder(); + } else { + if (options.quoting) { + if (options.escaping) { + delimiter = std::make_shared<LexingBoundaryFinder<true, true>>(options); + } else { + delimiter = std::make_shared<LexingBoundaryFinder<true, false>>(options); + } + } else { + if (options.escaping) { + delimiter = std::make_shared<LexingBoundaryFinder<false, true>>(options); + } else { + delimiter = std::make_shared<LexingBoundaryFinder<false, false>>(options); + } + } + } + return internal::make_unique<Chunker>(std::move(delimiter)); +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.h index bcebd6572e..662b16ec40 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/chunker.h @@ -1,36 +1,36 @@ -// 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 <cstdint> -#include <memory> - -#include "arrow/csv/options.h" -#include "arrow/status.h" -#include "arrow/util/delimiting.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace csv { - -ARROW_EXPORT -std::unique_ptr<Chunker> MakeChunker(const ParseOptions& options); - -} // namespace csv -} // namespace arrow +// 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 <cstdint> +#include <memory> + +#include "arrow/csv/options.h" +#include "arrow/status.h" +#include "arrow/util/delimiting.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace csv { + +ARROW_EXPORT +std::unique_ptr<Chunker> MakeChunker(const ParseOptions& options); + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.cc index 910ca1980c..bc97442873 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.cc @@ -1,367 +1,367 @@ -// 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. - -#include <cstddef> -#include <cstdint> -#include <memory> -#include <mutex> -#include <sstream> -#include <string> -#include <utility> -#include <vector> - -#include "arrow/array.h" -#include "arrow/array/builder_base.h" -#include "arrow/chunked_array.h" -#include "arrow/csv/column_builder.h" -#include "arrow/csv/converter.h" -#include "arrow/csv/inference_internal.h" -#include "arrow/csv/options.h" -#include "arrow/csv/parser.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_fwd.h" -#include "arrow/util/logging.h" -#include "arrow/util/task_group.h" - -namespace arrow { -namespace csv { - -class BlockParser; - -using internal::TaskGroup; - -class ConcreteColumnBuilder : public ColumnBuilder { - public: - explicit ConcreteColumnBuilder(MemoryPool* pool, - std::shared_ptr<internal::TaskGroup> task_group, - int32_t col_index = -1) - : ColumnBuilder(std::move(task_group)), pool_(pool), col_index_(col_index) {} - - void Append(const std::shared_ptr<BlockParser>& parser) override { - Insert(static_cast<int64_t>(chunks_.size()), parser); - } - - Result<std::shared_ptr<ChunkedArray>> Finish() override { - std::lock_guard<std::mutex> lock(mutex_); - - return FinishUnlocked(); - } - - protected: - virtual std::shared_ptr<DataType> type() const = 0; - - Result<std::shared_ptr<ChunkedArray>> FinishUnlocked() { - auto type = this->type(); - for (const auto& chunk : chunks_) { - if (chunk == nullptr) { - return Status::UnknownError("a chunk failed converting for an unknown reason"); - } - DCHECK_EQ(chunk->type()->id(), type->id()) << "Chunk types not equal!"; - } - return std::make_shared<ChunkedArray>(chunks_, std::move(type)); - } - - void ReserveChunks(int64_t block_index) { - // Create a null Array pointer at the back at the list. - std::lock_guard<std::mutex> lock(mutex_); - ReserveChunksUnlocked(block_index); - } - - void ReserveChunksUnlocked(int64_t block_index) { - // Create a null Array pointer at the back at the list. - size_t chunk_index = static_cast<size_t>(block_index); - if (chunks_.size() <= chunk_index) { - chunks_.resize(chunk_index + 1); - } - } - - Status SetChunk(int64_t chunk_index, Result<std::shared_ptr<Array>> maybe_array) { - std::lock_guard<std::mutex> lock(mutex_); - return SetChunkUnlocked(chunk_index, std::move(maybe_array)); - } - - Status SetChunkUnlocked(int64_t chunk_index, - Result<std::shared_ptr<Array>> maybe_array) { - // Should not insert an already built chunk - DCHECK_EQ(chunks_[chunk_index], nullptr); - - if (maybe_array.ok()) { - chunks_[chunk_index] = *std::move(maybe_array); - return Status::OK(); - } else { - return WrapConversionError(maybe_array.status()); - } - } - - Status WrapConversionError(const Status& st) { - if (ARROW_PREDICT_TRUE(st.ok())) { - return st; - } else { - std::stringstream ss; - ss << "In CSV column #" << col_index_ << ": " << st.message(); - return st.WithMessage(ss.str()); - } - } - - MemoryPool* pool_; - int32_t col_index_; - - ArrayVector chunks_; - - std::mutex mutex_; -}; - -////////////////////////////////////////////////////////////////////////// -// Null column builder implementation (for a column not in the CSV file) - -class NullColumnBuilder : public ConcreteColumnBuilder { - public: - explicit NullColumnBuilder(const std::shared_ptr<DataType>& type, MemoryPool* pool, - const std::shared_ptr<internal::TaskGroup>& task_group) - : ConcreteColumnBuilder(pool, task_group), type_(type) {} - - void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; - - protected: - std::shared_ptr<DataType> type() const override { return type_; } - - std::shared_ptr<DataType> type_; -}; - -void NullColumnBuilder::Insert(int64_t block_index, - const std::shared_ptr<BlockParser>& parser) { - ReserveChunks(block_index); - - // Spawn a task that will build an array of nulls with the right DataType - const int32_t num_rows = parser->num_rows(); - DCHECK_GE(num_rows, 0); - - task_group_->Append([=]() -> Status { - std::unique_ptr<ArrayBuilder> builder; - RETURN_NOT_OK(MakeBuilder(pool_, type_, &builder)); - std::shared_ptr<Array> res; - RETURN_NOT_OK(builder->AppendNulls(num_rows)); - RETURN_NOT_OK(builder->Finish(&res)); - - return SetChunk(block_index, res); - }); -} - -////////////////////////////////////////////////////////////////////////// -// Pre-typed column builder implementation - -class TypedColumnBuilder : public ConcreteColumnBuilder { - public: - TypedColumnBuilder(const std::shared_ptr<DataType>& type, int32_t col_index, - const ConvertOptions& options, MemoryPool* pool, - const std::shared_ptr<internal::TaskGroup>& task_group) - : ConcreteColumnBuilder(pool, task_group, col_index), - type_(type), - options_(options) {} - - Status Init(); - - void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; - - protected: - std::shared_ptr<DataType> type() const override { return type_; } - - std::shared_ptr<DataType> type_; - // CAUTION: ConvertOptions can grow large (if it customizes hundreds or - // thousands of columns), so avoid copying it in each TypedColumnBuilder. - const ConvertOptions& options_; - - std::shared_ptr<Converter> converter_; -}; - -Status TypedColumnBuilder::Init() { - ARROW_ASSIGN_OR_RAISE(converter_, Converter::Make(type_, options_, pool_)); - return Status::OK(); -} - -void TypedColumnBuilder::Insert(int64_t block_index, - const std::shared_ptr<BlockParser>& parser) { - DCHECK_NE(converter_, nullptr); - - ReserveChunks(block_index); - - // We're careful that all references in the closure outlive the Append() call - task_group_->Append([=]() -> Status { - return SetChunk(block_index, converter_->Convert(*parser, col_index_)); - }); -} - -////////////////////////////////////////////////////////////////////////// -// Type-inferring column builder implementation - -class InferringColumnBuilder : public ConcreteColumnBuilder { - public: - InferringColumnBuilder(int32_t col_index, const ConvertOptions& options, - MemoryPool* pool, - const std::shared_ptr<internal::TaskGroup>& task_group) - : ConcreteColumnBuilder(pool, task_group, col_index), - options_(options), - infer_status_(options) {} - - Status Init(); - - void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; - Result<std::shared_ptr<ChunkedArray>> Finish() override; - - protected: - std::shared_ptr<DataType> type() const override { - DCHECK_NE(converter_, nullptr); - return converter_->type(); - } - - Status UpdateType(); - Status TryConvertChunk(int64_t chunk_index); - // This must be called unlocked! - void ScheduleConvertChunk(int64_t chunk_index); - - // CAUTION: ConvertOptions can grow large (if it customizes hundreds or - // thousands of columns), so avoid copying it in each InferringColumnBuilder. - const ConvertOptions& options_; - - // Current inference status - InferStatus infer_status_; - std::shared_ptr<Converter> converter_; - - // The parsers corresponding to each chunk (for reconverting) - std::vector<std::shared_ptr<BlockParser>> parsers_; -}; - -Status InferringColumnBuilder::Init() { return UpdateType(); } - -Status InferringColumnBuilder::UpdateType() { - return infer_status_.MakeConverter(pool_).Value(&converter_); -} - -void InferringColumnBuilder::ScheduleConvertChunk(int64_t chunk_index) { - task_group_->Append([=]() { return TryConvertChunk(chunk_index); }); -} - -Status InferringColumnBuilder::TryConvertChunk(int64_t chunk_index) { - std::unique_lock<std::mutex> lock(mutex_); - std::shared_ptr<Converter> converter = converter_; - std::shared_ptr<BlockParser> parser = parsers_[chunk_index]; - InferKind kind = infer_status_.kind(); - - DCHECK_NE(parser, nullptr); - - lock.unlock(); - auto maybe_array = converter->Convert(*parser, col_index_); - lock.lock(); - - if (kind != infer_status_.kind()) { - // infer_kind_ was changed by another task, reconvert - lock.unlock(); - ScheduleConvertChunk(chunk_index); - return Status::OK(); - } - - if (maybe_array.ok() || !infer_status_.can_loosen_type()) { - // Conversion succeeded, or failed definitively - if (!infer_status_.can_loosen_type()) { - // We won't try to reconvert anymore - parsers_[chunk_index].reset(); - } - return SetChunkUnlocked(chunk_index, maybe_array); - } - - // Conversion failed, try another type - infer_status_.LoosenType(maybe_array.status()); - RETURN_NOT_OK(UpdateType()); - - // Reconvert past finished chunks - // (unfinished chunks will notice by themselves if they need reconverting) - const auto nchunks = static_cast<int64_t>(chunks_.size()); - for (int64_t i = 0; i < nchunks; ++i) { - if (i != chunk_index && chunks_[i]) { - // We're assuming the chunk was converted using the wrong type - // (which should be true unless the executor reorders tasks) - chunks_[i].reset(); - lock.unlock(); - ScheduleConvertChunk(i); - lock.lock(); - } - } - - // Reconvert this chunk - lock.unlock(); - ScheduleConvertChunk(chunk_index); - - return Status::OK(); -} - -void InferringColumnBuilder::Insert(int64_t block_index, - const std::shared_ptr<BlockParser>& parser) { - // Create a slot for the new chunk and spawn a task to convert it - size_t chunk_index = static_cast<size_t>(block_index); - { - std::lock_guard<std::mutex> lock(mutex_); - - DCHECK_NE(converter_, nullptr); - if (parsers_.size() <= chunk_index) { - parsers_.resize(chunk_index + 1); - } - // Should not insert an already converting chunk - DCHECK_EQ(parsers_[chunk_index], nullptr); - parsers_[chunk_index] = parser; - ReserveChunksUnlocked(block_index); - } - - ScheduleConvertChunk(chunk_index); -} - -Result<std::shared_ptr<ChunkedArray>> InferringColumnBuilder::Finish() { - std::lock_guard<std::mutex> lock(mutex_); - - parsers_.clear(); - return FinishUnlocked(); -} - -////////////////////////////////////////////////////////////////////////// -// Factory functions - -Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::Make( - MemoryPool* pool, const std::shared_ptr<DataType>& type, int32_t col_index, - const ConvertOptions& options, const std::shared_ptr<TaskGroup>& task_group) { - auto ptr = - std::make_shared<TypedColumnBuilder>(type, col_index, options, pool, task_group); - RETURN_NOT_OK(ptr->Init()); - return ptr; -} - -Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::Make( - MemoryPool* pool, int32_t col_index, const ConvertOptions& options, - const std::shared_ptr<TaskGroup>& task_group) { - auto ptr = - std::make_shared<InferringColumnBuilder>(col_index, options, pool, task_group); - RETURN_NOT_OK(ptr->Init()); - return ptr; -} - -Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::MakeNull( - MemoryPool* pool, const std::shared_ptr<DataType>& type, - const std::shared_ptr<internal::TaskGroup>& task_group) { - return std::make_shared<NullColumnBuilder>(type, pool, task_group); -} - -} // namespace csv -} // namespace arrow +// 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. + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <mutex> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include "arrow/array.h" +#include "arrow/array/builder_base.h" +#include "arrow/chunked_array.h" +#include "arrow/csv/column_builder.h" +#include "arrow/csv/converter.h" +#include "arrow/csv/inference_internal.h" +#include "arrow/csv/options.h" +#include "arrow/csv/parser.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_fwd.h" +#include "arrow/util/logging.h" +#include "arrow/util/task_group.h" + +namespace arrow { +namespace csv { + +class BlockParser; + +using internal::TaskGroup; + +class ConcreteColumnBuilder : public ColumnBuilder { + public: + explicit ConcreteColumnBuilder(MemoryPool* pool, + std::shared_ptr<internal::TaskGroup> task_group, + int32_t col_index = -1) + : ColumnBuilder(std::move(task_group)), pool_(pool), col_index_(col_index) {} + + void Append(const std::shared_ptr<BlockParser>& parser) override { + Insert(static_cast<int64_t>(chunks_.size()), parser); + } + + Result<std::shared_ptr<ChunkedArray>> Finish() override { + std::lock_guard<std::mutex> lock(mutex_); + + return FinishUnlocked(); + } + + protected: + virtual std::shared_ptr<DataType> type() const = 0; + + Result<std::shared_ptr<ChunkedArray>> FinishUnlocked() { + auto type = this->type(); + for (const auto& chunk : chunks_) { + if (chunk == nullptr) { + return Status::UnknownError("a chunk failed converting for an unknown reason"); + } + DCHECK_EQ(chunk->type()->id(), type->id()) << "Chunk types not equal!"; + } + return std::make_shared<ChunkedArray>(chunks_, std::move(type)); + } + + void ReserveChunks(int64_t block_index) { + // Create a null Array pointer at the back at the list. + std::lock_guard<std::mutex> lock(mutex_); + ReserveChunksUnlocked(block_index); + } + + void ReserveChunksUnlocked(int64_t block_index) { + // Create a null Array pointer at the back at the list. + size_t chunk_index = static_cast<size_t>(block_index); + if (chunks_.size() <= chunk_index) { + chunks_.resize(chunk_index + 1); + } + } + + Status SetChunk(int64_t chunk_index, Result<std::shared_ptr<Array>> maybe_array) { + std::lock_guard<std::mutex> lock(mutex_); + return SetChunkUnlocked(chunk_index, std::move(maybe_array)); + } + + Status SetChunkUnlocked(int64_t chunk_index, + Result<std::shared_ptr<Array>> maybe_array) { + // Should not insert an already built chunk + DCHECK_EQ(chunks_[chunk_index], nullptr); + + if (maybe_array.ok()) { + chunks_[chunk_index] = *std::move(maybe_array); + return Status::OK(); + } else { + return WrapConversionError(maybe_array.status()); + } + } + + Status WrapConversionError(const Status& st) { + if (ARROW_PREDICT_TRUE(st.ok())) { + return st; + } else { + std::stringstream ss; + ss << "In CSV column #" << col_index_ << ": " << st.message(); + return st.WithMessage(ss.str()); + } + } + + MemoryPool* pool_; + int32_t col_index_; + + ArrayVector chunks_; + + std::mutex mutex_; +}; + +////////////////////////////////////////////////////////////////////////// +// Null column builder implementation (for a column not in the CSV file) + +class NullColumnBuilder : public ConcreteColumnBuilder { + public: + explicit NullColumnBuilder(const std::shared_ptr<DataType>& type, MemoryPool* pool, + const std::shared_ptr<internal::TaskGroup>& task_group) + : ConcreteColumnBuilder(pool, task_group), type_(type) {} + + void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; + + protected: + std::shared_ptr<DataType> type() const override { return type_; } + + std::shared_ptr<DataType> type_; +}; + +void NullColumnBuilder::Insert(int64_t block_index, + const std::shared_ptr<BlockParser>& parser) { + ReserveChunks(block_index); + + // Spawn a task that will build an array of nulls with the right DataType + const int32_t num_rows = parser->num_rows(); + DCHECK_GE(num_rows, 0); + + task_group_->Append([=]() -> Status { + std::unique_ptr<ArrayBuilder> builder; + RETURN_NOT_OK(MakeBuilder(pool_, type_, &builder)); + std::shared_ptr<Array> res; + RETURN_NOT_OK(builder->AppendNulls(num_rows)); + RETURN_NOT_OK(builder->Finish(&res)); + + return SetChunk(block_index, res); + }); +} + +////////////////////////////////////////////////////////////////////////// +// Pre-typed column builder implementation + +class TypedColumnBuilder : public ConcreteColumnBuilder { + public: + TypedColumnBuilder(const std::shared_ptr<DataType>& type, int32_t col_index, + const ConvertOptions& options, MemoryPool* pool, + const std::shared_ptr<internal::TaskGroup>& task_group) + : ConcreteColumnBuilder(pool, task_group, col_index), + type_(type), + options_(options) {} + + Status Init(); + + void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; + + protected: + std::shared_ptr<DataType> type() const override { return type_; } + + std::shared_ptr<DataType> type_; + // CAUTION: ConvertOptions can grow large (if it customizes hundreds or + // thousands of columns), so avoid copying it in each TypedColumnBuilder. + const ConvertOptions& options_; + + std::shared_ptr<Converter> converter_; +}; + +Status TypedColumnBuilder::Init() { + ARROW_ASSIGN_OR_RAISE(converter_, Converter::Make(type_, options_, pool_)); + return Status::OK(); +} + +void TypedColumnBuilder::Insert(int64_t block_index, + const std::shared_ptr<BlockParser>& parser) { + DCHECK_NE(converter_, nullptr); + + ReserveChunks(block_index); + + // We're careful that all references in the closure outlive the Append() call + task_group_->Append([=]() -> Status { + return SetChunk(block_index, converter_->Convert(*parser, col_index_)); + }); +} + +////////////////////////////////////////////////////////////////////////// +// Type-inferring column builder implementation + +class InferringColumnBuilder : public ConcreteColumnBuilder { + public: + InferringColumnBuilder(int32_t col_index, const ConvertOptions& options, + MemoryPool* pool, + const std::shared_ptr<internal::TaskGroup>& task_group) + : ConcreteColumnBuilder(pool, task_group, col_index), + options_(options), + infer_status_(options) {} + + Status Init(); + + void Insert(int64_t block_index, const std::shared_ptr<BlockParser>& parser) override; + Result<std::shared_ptr<ChunkedArray>> Finish() override; + + protected: + std::shared_ptr<DataType> type() const override { + DCHECK_NE(converter_, nullptr); + return converter_->type(); + } + + Status UpdateType(); + Status TryConvertChunk(int64_t chunk_index); + // This must be called unlocked! + void ScheduleConvertChunk(int64_t chunk_index); + + // CAUTION: ConvertOptions can grow large (if it customizes hundreds or + // thousands of columns), so avoid copying it in each InferringColumnBuilder. + const ConvertOptions& options_; + + // Current inference status + InferStatus infer_status_; + std::shared_ptr<Converter> converter_; + + // The parsers corresponding to each chunk (for reconverting) + std::vector<std::shared_ptr<BlockParser>> parsers_; +}; + +Status InferringColumnBuilder::Init() { return UpdateType(); } + +Status InferringColumnBuilder::UpdateType() { + return infer_status_.MakeConverter(pool_).Value(&converter_); +} + +void InferringColumnBuilder::ScheduleConvertChunk(int64_t chunk_index) { + task_group_->Append([=]() { return TryConvertChunk(chunk_index); }); +} + +Status InferringColumnBuilder::TryConvertChunk(int64_t chunk_index) { + std::unique_lock<std::mutex> lock(mutex_); + std::shared_ptr<Converter> converter = converter_; + std::shared_ptr<BlockParser> parser = parsers_[chunk_index]; + InferKind kind = infer_status_.kind(); + + DCHECK_NE(parser, nullptr); + + lock.unlock(); + auto maybe_array = converter->Convert(*parser, col_index_); + lock.lock(); + + if (kind != infer_status_.kind()) { + // infer_kind_ was changed by another task, reconvert + lock.unlock(); + ScheduleConvertChunk(chunk_index); + return Status::OK(); + } + + if (maybe_array.ok() || !infer_status_.can_loosen_type()) { + // Conversion succeeded, or failed definitively + if (!infer_status_.can_loosen_type()) { + // We won't try to reconvert anymore + parsers_[chunk_index].reset(); + } + return SetChunkUnlocked(chunk_index, maybe_array); + } + + // Conversion failed, try another type + infer_status_.LoosenType(maybe_array.status()); + RETURN_NOT_OK(UpdateType()); + + // Reconvert past finished chunks + // (unfinished chunks will notice by themselves if they need reconverting) + const auto nchunks = static_cast<int64_t>(chunks_.size()); + for (int64_t i = 0; i < nchunks; ++i) { + if (i != chunk_index && chunks_[i]) { + // We're assuming the chunk was converted using the wrong type + // (which should be true unless the executor reorders tasks) + chunks_[i].reset(); + lock.unlock(); + ScheduleConvertChunk(i); + lock.lock(); + } + } + + // Reconvert this chunk + lock.unlock(); + ScheduleConvertChunk(chunk_index); + + return Status::OK(); +} + +void InferringColumnBuilder::Insert(int64_t block_index, + const std::shared_ptr<BlockParser>& parser) { + // Create a slot for the new chunk and spawn a task to convert it + size_t chunk_index = static_cast<size_t>(block_index); + { + std::lock_guard<std::mutex> lock(mutex_); + + DCHECK_NE(converter_, nullptr); + if (parsers_.size() <= chunk_index) { + parsers_.resize(chunk_index + 1); + } + // Should not insert an already converting chunk + DCHECK_EQ(parsers_[chunk_index], nullptr); + parsers_[chunk_index] = parser; + ReserveChunksUnlocked(block_index); + } + + ScheduleConvertChunk(chunk_index); +} + +Result<std::shared_ptr<ChunkedArray>> InferringColumnBuilder::Finish() { + std::lock_guard<std::mutex> lock(mutex_); + + parsers_.clear(); + return FinishUnlocked(); +} + +////////////////////////////////////////////////////////////////////////// +// Factory functions + +Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::Make( + MemoryPool* pool, const std::shared_ptr<DataType>& type, int32_t col_index, + const ConvertOptions& options, const std::shared_ptr<TaskGroup>& task_group) { + auto ptr = + std::make_shared<TypedColumnBuilder>(type, col_index, options, pool, task_group); + RETURN_NOT_OK(ptr->Init()); + return ptr; +} + +Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::Make( + MemoryPool* pool, int32_t col_index, const ConvertOptions& options, + const std::shared_ptr<TaskGroup>& task_group) { + auto ptr = + std::make_shared<InferringColumnBuilder>(col_index, options, pool, task_group); + RETURN_NOT_OK(ptr->Init()); + return ptr; +} + +Result<std::shared_ptr<ColumnBuilder>> ColumnBuilder::MakeNull( + MemoryPool* pool, const std::shared_ptr<DataType>& type, + const std::shared_ptr<internal::TaskGroup>& task_group) { + return std::make_shared<NullColumnBuilder>(type, pool, task_group); +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.h index 72bb46586e..170a8ad067 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_builder.h @@ -1,78 +1,78 @@ -// 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 <cstdint> -#include <memory> -#include <utility> - -#include "arrow/result.h" -#include "arrow/type_fwd.h" -#include "arrow/util/type_fwd.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace csv { - -class BlockParser; -struct ConvertOptions; - -class ARROW_EXPORT ColumnBuilder { - public: - virtual ~ColumnBuilder() = default; - - /// Spawn a task that will try to convert and append the given CSV block. - /// All calls to Append() should happen on the same thread, otherwise - /// call Insert() instead. - virtual void Append(const std::shared_ptr<BlockParser>& parser) = 0; - - /// Spawn a task that will try to convert and insert the given CSV block - virtual void Insert(int64_t block_index, - const std::shared_ptr<BlockParser>& parser) = 0; - - /// Return the final chunked array. The TaskGroup _must_ have finished! - virtual Result<std::shared_ptr<ChunkedArray>> Finish() = 0; - - std::shared_ptr<internal::TaskGroup> task_group() { return task_group_; } - - /// Construct a strictly-typed ColumnBuilder. - static Result<std::shared_ptr<ColumnBuilder>> Make( - MemoryPool* pool, const std::shared_ptr<DataType>& type, int32_t col_index, - const ConvertOptions& options, - const std::shared_ptr<internal::TaskGroup>& task_group); - - /// Construct a type-inferring ColumnBuilder. - static Result<std::shared_ptr<ColumnBuilder>> Make( - MemoryPool* pool, int32_t col_index, const ConvertOptions& options, - const std::shared_ptr<internal::TaskGroup>& task_group); - - /// Construct a ColumnBuilder for a column of nulls - /// (i.e. not present in the CSV file). - static Result<std::shared_ptr<ColumnBuilder>> MakeNull( - MemoryPool* pool, const std::shared_ptr<DataType>& type, - const std::shared_ptr<internal::TaskGroup>& task_group); - - protected: - explicit ColumnBuilder(std::shared_ptr<internal::TaskGroup> task_group) - : task_group_(std::move(task_group)) {} - - std::shared_ptr<internal::TaskGroup> task_group_; -}; - -} // namespace csv -} // namespace arrow +// 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 <cstdint> +#include <memory> +#include <utility> + +#include "arrow/result.h" +#include "arrow/type_fwd.h" +#include "arrow/util/type_fwd.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace csv { + +class BlockParser; +struct ConvertOptions; + +class ARROW_EXPORT ColumnBuilder { + public: + virtual ~ColumnBuilder() = default; + + /// Spawn a task that will try to convert and append the given CSV block. + /// All calls to Append() should happen on the same thread, otherwise + /// call Insert() instead. + virtual void Append(const std::shared_ptr<BlockParser>& parser) = 0; + + /// Spawn a task that will try to convert and insert the given CSV block + virtual void Insert(int64_t block_index, + const std::shared_ptr<BlockParser>& parser) = 0; + + /// Return the final chunked array. The TaskGroup _must_ have finished! + virtual Result<std::shared_ptr<ChunkedArray>> Finish() = 0; + + std::shared_ptr<internal::TaskGroup> task_group() { return task_group_; } + + /// Construct a strictly-typed ColumnBuilder. + static Result<std::shared_ptr<ColumnBuilder>> Make( + MemoryPool* pool, const std::shared_ptr<DataType>& type, int32_t col_index, + const ConvertOptions& options, + const std::shared_ptr<internal::TaskGroup>& task_group); + + /// Construct a type-inferring ColumnBuilder. + static Result<std::shared_ptr<ColumnBuilder>> Make( + MemoryPool* pool, int32_t col_index, const ConvertOptions& options, + const std::shared_ptr<internal::TaskGroup>& task_group); + + /// Construct a ColumnBuilder for a column of nulls + /// (i.e. not present in the CSV file). + static Result<std::shared_ptr<ColumnBuilder>> MakeNull( + MemoryPool* pool, const std::shared_ptr<DataType>& type, + const std::shared_ptr<internal::TaskGroup>& task_group); + + protected: + explicit ColumnBuilder(std::shared_ptr<internal::TaskGroup> task_group) + : task_group_(std::move(task_group)) {} + + std::shared_ptr<internal::TaskGroup> task_group_; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.cc index 70d8e90b35..436d703a9c 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.cc @@ -1,243 +1,243 @@ -// 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. - -#include "arrow/csv/column_decoder.h" - -#include <cstddef> -#include <cstdint> -#include <memory> -#include <sstream> -#include <string> -#include <utility> - -#include "arrow/array.h" -#include "arrow/array/builder_base.h" -#include "arrow/csv/converter.h" -#include "arrow/csv/inference_internal.h" -#include "arrow/csv/options.h" -#include "arrow/csv/parser.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_fwd.h" -#include "arrow/util/future.h" -#include "arrow/util/logging.h" -#include "arrow/util/task_group.h" - -namespace arrow { -namespace csv { - -using internal::TaskGroup; - -class ConcreteColumnDecoder : public ColumnDecoder { - public: - explicit ConcreteColumnDecoder(MemoryPool* pool, int32_t col_index = -1) - : ColumnDecoder(), pool_(pool), col_index_(col_index) {} - - protected: - // XXX useful? - virtual std::shared_ptr<DataType> type() const = 0; - - Result<std::shared_ptr<Array>> WrapConversionError( - const Result<std::shared_ptr<Array>>& result) { - if (ARROW_PREDICT_TRUE(result.ok())) { - return result; - } else { - const auto& st = result.status(); - std::stringstream ss; - ss << "In CSV column #" << col_index_ << ": " << st.message(); - return st.WithMessage(ss.str()); - } - } - - MemoryPool* pool_; - int32_t col_index_; - internal::Executor* executor_; -}; - -////////////////////////////////////////////////////////////////////////// -// Null column decoder implementation (for a column not in the CSV file) - -class NullColumnDecoder : public ConcreteColumnDecoder { - public: - explicit NullColumnDecoder(const std::shared_ptr<DataType>& type, MemoryPool* pool) - : ConcreteColumnDecoder(pool), type_(type) {} - - Future<std::shared_ptr<Array>> Decode( - const std::shared_ptr<BlockParser>& parser) override; - - protected: - std::shared_ptr<DataType> type() const override { return type_; } - - std::shared_ptr<DataType> type_; -}; - -Future<std::shared_ptr<Array>> NullColumnDecoder::Decode( - const std::shared_ptr<BlockParser>& parser) { - DCHECK_GE(parser->num_rows(), 0); - return WrapConversionError(MakeArrayOfNull(type_, parser->num_rows(), pool_)); -} - -////////////////////////////////////////////////////////////////////////// -// Pre-typed column decoder implementation - -class TypedColumnDecoder : public ConcreteColumnDecoder { - public: - TypedColumnDecoder(const std::shared_ptr<DataType>& type, int32_t col_index, - const ConvertOptions& options, MemoryPool* pool) - : ConcreteColumnDecoder(pool, col_index), type_(type), options_(options) {} - - Status Init(); - - Future<std::shared_ptr<Array>> Decode( - const std::shared_ptr<BlockParser>& parser) override; - - protected: - std::shared_ptr<DataType> type() const override { return type_; } - - std::shared_ptr<DataType> type_; - // CAUTION: ConvertOptions can grow large (if it customizes hundreds or - // thousands of columns), so avoid copying it in each TypedColumnDecoder. - const ConvertOptions& options_; - - std::shared_ptr<Converter> converter_; -}; - -Status TypedColumnDecoder::Init() { - ARROW_ASSIGN_OR_RAISE(converter_, Converter::Make(type_, options_, pool_)); - return Status::OK(); -} - -Future<std::shared_ptr<Array>> TypedColumnDecoder::Decode( - const std::shared_ptr<BlockParser>& parser) { - DCHECK_NE(converter_, nullptr); - return Future<std::shared_ptr<Array>>::MakeFinished( - WrapConversionError(converter_->Convert(*parser, col_index_))); -} - -////////////////////////////////////////////////////////////////////////// -// Type-inferring column builder implementation - -class InferringColumnDecoder : public ConcreteColumnDecoder { - public: - InferringColumnDecoder(int32_t col_index, const ConvertOptions& options, - MemoryPool* pool) - : ConcreteColumnDecoder(pool, col_index), - options_(options), - infer_status_(options), - type_frozen_(false) { - first_inference_run_ = Future<>::Make(); - first_inferrer_ = 0; - } - - Status Init(); - - Future<std::shared_ptr<Array>> Decode( - const std::shared_ptr<BlockParser>& parser) override; - - protected: - std::shared_ptr<DataType> type() const override { - DCHECK_NE(converter_, nullptr); - return converter_->type(); - } - - Status UpdateType(); - Result<std::shared_ptr<Array>> RunInference(const std::shared_ptr<BlockParser>& parser); - - // CAUTION: ConvertOptions can grow large (if it customizes hundreds or - // thousands of columns), so avoid copying it in each InferringColumnDecoder. - const ConvertOptions& options_; - - // Current inference status - InferStatus infer_status_; - bool type_frozen_; - std::atomic<int> first_inferrer_; - Future<> first_inference_run_; - std::shared_ptr<Converter> converter_; -}; - -Status InferringColumnDecoder::Init() { return UpdateType(); } - -Status InferringColumnDecoder::UpdateType() { - return infer_status_.MakeConverter(pool_).Value(&converter_); -} - -Result<std::shared_ptr<Array>> InferringColumnDecoder::RunInference( - const std::shared_ptr<BlockParser>& parser) { - while (true) { - // (no one else should be updating converter_ concurrently) - auto maybe_array = converter_->Convert(*parser, col_index_); - - if (maybe_array.ok() || !infer_status_.can_loosen_type()) { - // Conversion succeeded, or failed definitively - DCHECK(!type_frozen_); - type_frozen_ = true; - return maybe_array; - } - // Conversion failed temporarily, try another type - infer_status_.LoosenType(maybe_array.status()); - auto update_status = UpdateType(); - if (!update_status.ok()) { - return update_status; - } - } -} - -Future<std::shared_ptr<Array>> InferringColumnDecoder::Decode( - const std::shared_ptr<BlockParser>& parser) { - bool already_taken = first_inferrer_.fetch_or(1); - // First block: run inference - if (!already_taken) { - auto maybe_array = RunInference(parser); - first_inference_run_.MarkFinished(); - return Future<std::shared_ptr<Array>>::MakeFinished(std::move(maybe_array)); - } - - // Non-first block: wait for inference to finish on first block now, - // without blocking a TaskGroup thread. - return first_inference_run_.Then([this, parser] { - DCHECK(type_frozen_); - auto maybe_array = converter_->Convert(*parser, col_index_); - return WrapConversionError(converter_->Convert(*parser, col_index_)); - }); -} - -////////////////////////////////////////////////////////////////////////// -// Factory functions - -Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::Make( - MemoryPool* pool, int32_t col_index, const ConvertOptions& options) { - auto ptr = std::make_shared<InferringColumnDecoder>(col_index, options, pool); - RETURN_NOT_OK(ptr->Init()); - return ptr; -} - -Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::Make( - MemoryPool* pool, std::shared_ptr<DataType> type, int32_t col_index, - const ConvertOptions& options) { - auto ptr = - std::make_shared<TypedColumnDecoder>(std::move(type), col_index, options, pool); - RETURN_NOT_OK(ptr->Init()); - return ptr; -} - -Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::MakeNull( - MemoryPool* pool, std::shared_ptr<DataType> type) { - return std::make_shared<NullColumnDecoder>(std::move(type), pool); -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/column_decoder.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <sstream> +#include <string> +#include <utility> + +#include "arrow/array.h" +#include "arrow/array/builder_base.h" +#include "arrow/csv/converter.h" +#include "arrow/csv/inference_internal.h" +#include "arrow/csv/options.h" +#include "arrow/csv/parser.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_fwd.h" +#include "arrow/util/future.h" +#include "arrow/util/logging.h" +#include "arrow/util/task_group.h" + +namespace arrow { +namespace csv { + +using internal::TaskGroup; + +class ConcreteColumnDecoder : public ColumnDecoder { + public: + explicit ConcreteColumnDecoder(MemoryPool* pool, int32_t col_index = -1) + : ColumnDecoder(), pool_(pool), col_index_(col_index) {} + + protected: + // XXX useful? + virtual std::shared_ptr<DataType> type() const = 0; + + Result<std::shared_ptr<Array>> WrapConversionError( + const Result<std::shared_ptr<Array>>& result) { + if (ARROW_PREDICT_TRUE(result.ok())) { + return result; + } else { + const auto& st = result.status(); + std::stringstream ss; + ss << "In CSV column #" << col_index_ << ": " << st.message(); + return st.WithMessage(ss.str()); + } + } + + MemoryPool* pool_; + int32_t col_index_; + internal::Executor* executor_; +}; + +////////////////////////////////////////////////////////////////////////// +// Null column decoder implementation (for a column not in the CSV file) + +class NullColumnDecoder : public ConcreteColumnDecoder { + public: + explicit NullColumnDecoder(const std::shared_ptr<DataType>& type, MemoryPool* pool) + : ConcreteColumnDecoder(pool), type_(type) {} + + Future<std::shared_ptr<Array>> Decode( + const std::shared_ptr<BlockParser>& parser) override; + + protected: + std::shared_ptr<DataType> type() const override { return type_; } + + std::shared_ptr<DataType> type_; +}; + +Future<std::shared_ptr<Array>> NullColumnDecoder::Decode( + const std::shared_ptr<BlockParser>& parser) { + DCHECK_GE(parser->num_rows(), 0); + return WrapConversionError(MakeArrayOfNull(type_, parser->num_rows(), pool_)); +} + +////////////////////////////////////////////////////////////////////////// +// Pre-typed column decoder implementation + +class TypedColumnDecoder : public ConcreteColumnDecoder { + public: + TypedColumnDecoder(const std::shared_ptr<DataType>& type, int32_t col_index, + const ConvertOptions& options, MemoryPool* pool) + : ConcreteColumnDecoder(pool, col_index), type_(type), options_(options) {} + + Status Init(); + + Future<std::shared_ptr<Array>> Decode( + const std::shared_ptr<BlockParser>& parser) override; + + protected: + std::shared_ptr<DataType> type() const override { return type_; } + + std::shared_ptr<DataType> type_; + // CAUTION: ConvertOptions can grow large (if it customizes hundreds or + // thousands of columns), so avoid copying it in each TypedColumnDecoder. + const ConvertOptions& options_; + + std::shared_ptr<Converter> converter_; +}; + +Status TypedColumnDecoder::Init() { + ARROW_ASSIGN_OR_RAISE(converter_, Converter::Make(type_, options_, pool_)); + return Status::OK(); +} + +Future<std::shared_ptr<Array>> TypedColumnDecoder::Decode( + const std::shared_ptr<BlockParser>& parser) { + DCHECK_NE(converter_, nullptr); + return Future<std::shared_ptr<Array>>::MakeFinished( + WrapConversionError(converter_->Convert(*parser, col_index_))); +} + +////////////////////////////////////////////////////////////////////////// +// Type-inferring column builder implementation + +class InferringColumnDecoder : public ConcreteColumnDecoder { + public: + InferringColumnDecoder(int32_t col_index, const ConvertOptions& options, + MemoryPool* pool) + : ConcreteColumnDecoder(pool, col_index), + options_(options), + infer_status_(options), + type_frozen_(false) { + first_inference_run_ = Future<>::Make(); + first_inferrer_ = 0; + } + + Status Init(); + + Future<std::shared_ptr<Array>> Decode( + const std::shared_ptr<BlockParser>& parser) override; + + protected: + std::shared_ptr<DataType> type() const override { + DCHECK_NE(converter_, nullptr); + return converter_->type(); + } + + Status UpdateType(); + Result<std::shared_ptr<Array>> RunInference(const std::shared_ptr<BlockParser>& parser); + + // CAUTION: ConvertOptions can grow large (if it customizes hundreds or + // thousands of columns), so avoid copying it in each InferringColumnDecoder. + const ConvertOptions& options_; + + // Current inference status + InferStatus infer_status_; + bool type_frozen_; + std::atomic<int> first_inferrer_; + Future<> first_inference_run_; + std::shared_ptr<Converter> converter_; +}; + +Status InferringColumnDecoder::Init() { return UpdateType(); } + +Status InferringColumnDecoder::UpdateType() { + return infer_status_.MakeConverter(pool_).Value(&converter_); +} + +Result<std::shared_ptr<Array>> InferringColumnDecoder::RunInference( + const std::shared_ptr<BlockParser>& parser) { + while (true) { + // (no one else should be updating converter_ concurrently) + auto maybe_array = converter_->Convert(*parser, col_index_); + + if (maybe_array.ok() || !infer_status_.can_loosen_type()) { + // Conversion succeeded, or failed definitively + DCHECK(!type_frozen_); + type_frozen_ = true; + return maybe_array; + } + // Conversion failed temporarily, try another type + infer_status_.LoosenType(maybe_array.status()); + auto update_status = UpdateType(); + if (!update_status.ok()) { + return update_status; + } + } +} + +Future<std::shared_ptr<Array>> InferringColumnDecoder::Decode( + const std::shared_ptr<BlockParser>& parser) { + bool already_taken = first_inferrer_.fetch_or(1); + // First block: run inference + if (!already_taken) { + auto maybe_array = RunInference(parser); + first_inference_run_.MarkFinished(); + return Future<std::shared_ptr<Array>>::MakeFinished(std::move(maybe_array)); + } + + // Non-first block: wait for inference to finish on first block now, + // without blocking a TaskGroup thread. + return first_inference_run_.Then([this, parser] { + DCHECK(type_frozen_); + auto maybe_array = converter_->Convert(*parser, col_index_); + return WrapConversionError(converter_->Convert(*parser, col_index_)); + }); +} + +////////////////////////////////////////////////////////////////////////// +// Factory functions + +Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::Make( + MemoryPool* pool, int32_t col_index, const ConvertOptions& options) { + auto ptr = std::make_shared<InferringColumnDecoder>(col_index, options, pool); + RETURN_NOT_OK(ptr->Init()); + return ptr; +} + +Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::Make( + MemoryPool* pool, std::shared_ptr<DataType> type, int32_t col_index, + const ConvertOptions& options) { + auto ptr = + std::make_shared<TypedColumnDecoder>(std::move(type), col_index, options, pool); + RETURN_NOT_OK(ptr->Init()); + return ptr; +} + +Result<std::shared_ptr<ColumnDecoder>> ColumnDecoder::MakeNull( + MemoryPool* pool, std::shared_ptr<DataType> type) { + return std::make_shared<NullColumnDecoder>(std::move(type), pool); +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.h index 1b72573dee..5fbbd5df58 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/column_decoder.h @@ -1,64 +1,64 @@ -// 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 <cstdint> -#include <memory> -#include <utility> - -#include "arrow/result.h" -#include "arrow/type_fwd.h" -#include "arrow/util/type_fwd.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace csv { - -class BlockParser; -struct ConvertOptions; - -class ARROW_EXPORT ColumnDecoder { - public: - virtual ~ColumnDecoder() = default; - - /// Spawn a task that will try to convert and insert the given CSV block - virtual Future<std::shared_ptr<Array>> Decode( - const std::shared_ptr<BlockParser>& parser) = 0; - - /// Construct a strictly-typed ColumnDecoder. - static Result<std::shared_ptr<ColumnDecoder>> Make(MemoryPool* pool, - std::shared_ptr<DataType> type, - int32_t col_index, - const ConvertOptions& options); - - /// Construct a type-inferring ColumnDecoder. - /// Inference will run only on the first block, the type will be frozen afterwards. - static Result<std::shared_ptr<ColumnDecoder>> Make(MemoryPool* pool, int32_t col_index, - const ConvertOptions& options); - - /// Construct a ColumnDecoder for a column of nulls - /// (i.e. not present in the CSV file). - static Result<std::shared_ptr<ColumnDecoder>> MakeNull(MemoryPool* pool, - std::shared_ptr<DataType> type); - - protected: - ColumnDecoder() = default; -}; - -} // namespace csv -} // namespace arrow +// 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 <cstdint> +#include <memory> +#include <utility> + +#include "arrow/result.h" +#include "arrow/type_fwd.h" +#include "arrow/util/type_fwd.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace csv { + +class BlockParser; +struct ConvertOptions; + +class ARROW_EXPORT ColumnDecoder { + public: + virtual ~ColumnDecoder() = default; + + /// Spawn a task that will try to convert and insert the given CSV block + virtual Future<std::shared_ptr<Array>> Decode( + const std::shared_ptr<BlockParser>& parser) = 0; + + /// Construct a strictly-typed ColumnDecoder. + static Result<std::shared_ptr<ColumnDecoder>> Make(MemoryPool* pool, + std::shared_ptr<DataType> type, + int32_t col_index, + const ConvertOptions& options); + + /// Construct a type-inferring ColumnDecoder. + /// Inference will run only on the first block, the type will be frozen afterwards. + static Result<std::shared_ptr<ColumnDecoder>> Make(MemoryPool* pool, int32_t col_index, + const ConvertOptions& options); + + /// Construct a ColumnDecoder for a column of nulls + /// (i.e. not present in the CSV file). + static Result<std::shared_ptr<ColumnDecoder>> MakeNull(MemoryPool* pool, + std::shared_ptr<DataType> type); + + protected: + ColumnDecoder() = default; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.cc index 5d0386c6ca..cb72b22b40 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.cc @@ -1,692 +1,692 @@ -// 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. - -#include "arrow/csv/converter.h" - -#include <cstring> -#include <limits> -#include <sstream> -#include <string> -#include <type_traits> -#include <vector> - -#include "arrow/array/builder_binary.h" -#include "arrow/array/builder_decimal.h" -#include "arrow/array/builder_dict.h" -#include "arrow/array/builder_primitive.h" -#include "arrow/csv/parser.h" -#include "arrow/status.h" -#include "arrow/type.h" -#include "arrow/type_fwd.h" -#include "arrow/type_traits.h" -#include "arrow/util/checked_cast.h" -#include "arrow/util/decimal.h" -#include "arrow/util/trie.h" -#include "arrow/util/utf8.h" -#include "arrow/util/value_parsing.h" // IWYU pragma: keep - -namespace arrow { -namespace csv { - -using internal::checked_cast; -using internal::Trie; -using internal::TrieBuilder; - -namespace { - -Status GenericConversionError(const std::shared_ptr<DataType>& type, const uint8_t* data, - uint32_t size) { - return Status::Invalid("CSV conversion error to ", type->ToString(), - ": invalid value '", - std::string(reinterpret_cast<const char*>(data), size), "'"); -} - -inline bool IsWhitespace(uint8_t c) { - if (ARROW_PREDICT_TRUE(c > ' ')) { - return false; - } - return c == ' ' || c == '\t'; -} - -// Updates data_inout and size_inout to not include leading/trailing whitespace -// characters. -inline void TrimWhiteSpace(const uint8_t** data_inout, uint32_t* size_inout) { - const uint8_t*& data = *data_inout; - uint32_t& size = *size_inout; - // Skip trailing whitespace - if (ARROW_PREDICT_TRUE(size > 0) && ARROW_PREDICT_FALSE(IsWhitespace(data[size - 1]))) { - const uint8_t* p = data + size - 1; - while (size > 0 && IsWhitespace(*p)) { - --size; - --p; - } - } - // Skip leading whitespace - if (ARROW_PREDICT_TRUE(size > 0) && ARROW_PREDICT_FALSE(IsWhitespace(data[0]))) { - while (size > 0 && IsWhitespace(*data)) { - --size; - ++data; - } - } -} - -Status InitializeTrie(const std::vector<std::string>& inputs, Trie* trie) { - TrieBuilder builder; - for (const auto& s : inputs) { - RETURN_NOT_OK(builder.Append(s, true /* allow_duplicates */)); - } - *trie = builder.Finish(); - return Status::OK(); -} - -// Presize a builder based on parser contents -template <typename BuilderType> -enable_if_t<!is_base_binary_type<typename BuilderType::TypeClass>::value, Status> -PresizeBuilder(const BlockParser& parser, BuilderType* builder) { - return builder->Resize(parser.num_rows()); -} - -// Same, for variable-sized binary builders -template <typename T> -Status PresizeBuilder(const BlockParser& parser, BaseBinaryBuilder<T>* builder) { - RETURN_NOT_OK(builder->Resize(parser.num_rows())); - return builder->ReserveData(parser.num_bytes()); -} - -///////////////////////////////////////////////////////////////////////// -// Per-type value decoders - -struct ValueDecoder { - explicit ValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : type_(type), options_(options) {} - - Status Initialize() { - // TODO no need to build a separate Trie for each instance - return InitializeTrie(options_.null_values, &null_trie_); - } - - bool IsNull(const uint8_t* data, uint32_t size, bool quoted) { - if (quoted) { - return false; - } - return null_trie_.Find( - util::string_view(reinterpret_cast<const char*>(data), size)) >= 0; - } - - protected: - Trie null_trie_; - std::shared_ptr<DataType> type_; - const ConvertOptions& options_; -}; - -// -// Value decoder for fixed-size binary -// - -struct FixedSizeBinaryValueDecoder : public ValueDecoder { - using value_type = const uint8_t*; - - explicit FixedSizeBinaryValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : ValueDecoder(type, options), - byte_width_(checked_cast<const FixedSizeBinaryType&>(*type).byte_width()) {} - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - if (ARROW_PREDICT_FALSE(size != byte_width_)) { - return Status::Invalid("CSV conversion error to ", type_->ToString(), ": got a ", - size, "-byte long string"); - } - *out = data; - return Status::OK(); - } - - protected: - const uint32_t byte_width_; -}; - -// -// Value decoder for variable-size binary -// - -template <bool CheckUTF8> -struct BinaryValueDecoder : public ValueDecoder { - using value_type = util::string_view; - - using ValueDecoder::ValueDecoder; - - Status Initialize() { - util::InitializeUTF8(); - return ValueDecoder::Initialize(); - } - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - if (CheckUTF8 && ARROW_PREDICT_FALSE(!util::ValidateUTF8(data, size))) { - return Status::Invalid("CSV conversion error to ", type_->ToString(), - ": invalid UTF8 data"); - } - *out = {reinterpret_cast<const char*>(data), size}; - return Status::OK(); - } - - bool IsNull(const uint8_t* data, uint32_t size, bool quoted) { - return options_.strings_can_be_null && - (!quoted || options_.quoted_strings_can_be_null) && - ValueDecoder::IsNull(data, size, false /* quoted */); - } -}; - -// -// Value decoder for integers and floats -// - -template <typename T> -struct NumericValueDecoder : public ValueDecoder { - using value_type = typename T::c_type; - - using ValueDecoder::ValueDecoder; - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - // XXX should quoted values be allowed at all? - TrimWhiteSpace(&data, &size); - if (ARROW_PREDICT_FALSE( - !internal::ParseValue<T>(reinterpret_cast<const char*>(data), size, out))) { - return GenericConversionError(type_, data, size); - } - return Status::OK(); - } -}; - -// -// Value decoder for booleans -// - -struct BooleanValueDecoder : public ValueDecoder { - using value_type = bool; - - using ValueDecoder::ValueDecoder; - - Status Initialize() { - // TODO no need to build separate Tries for each instance - RETURN_NOT_OK(InitializeTrie(options_.true_values, &true_trie_)); - RETURN_NOT_OK(InitializeTrie(options_.false_values, &false_trie_)); - return ValueDecoder::Initialize(); - } - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - // XXX should quoted values be allowed at all? - if (false_trie_.Find(util::string_view(reinterpret_cast<const char*>(data), size)) >= - 0) { - *out = false; - return Status::OK(); - } - if (ARROW_PREDICT_TRUE(true_trie_.Find(util::string_view( - reinterpret_cast<const char*>(data), size)) >= 0)) { - *out = true; - return Status::OK(); - } - return GenericConversionError(type_, data, size); - } - - protected: - Trie true_trie_; - Trie false_trie_; -}; - -// -// Value decoder for decimals -// - -struct DecimalValueDecoder : public ValueDecoder { - using value_type = Decimal128; - - explicit DecimalValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : ValueDecoder(type, options), - decimal_type_(internal::checked_cast<const DecimalType&>(*type_)), - type_precision_(decimal_type_.precision()), - type_scale_(decimal_type_.scale()) {} - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - TrimWhiteSpace(&data, &size); - Decimal128 decimal; - int32_t precision, scale; - util::string_view view(reinterpret_cast<const char*>(data), size); - RETURN_NOT_OK(Decimal128::FromString(view, &decimal, &precision, &scale)); - if (precision > type_precision_) { - return Status::Invalid("Error converting '", view, "' to ", type_->ToString(), - ": precision not supported by type."); - } - if (scale != type_scale_) { - ARROW_ASSIGN_OR_RAISE(*out, decimal.Rescale(scale, type_scale_)); - } else { - *out = std::move(decimal); - } - return Status::OK(); - } - - protected: - const DecimalType& decimal_type_; - const int32_t type_precision_; - const int32_t type_scale_; -}; - -// -// Value decoders for timestamps -// - -struct InlineISO8601ValueDecoder : public ValueDecoder { - using value_type = int64_t; - - explicit InlineISO8601ValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : ValueDecoder(type, options), - unit_(checked_cast<const TimestampType&>(*type_).unit()) {} - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - if (ARROW_PREDICT_FALSE(!internal::ParseTimestampISO8601( - reinterpret_cast<const char*>(data), size, unit_, out))) { - return GenericConversionError(type_, data, size); - } - return Status::OK(); - } - - protected: - TimeUnit::type unit_; -}; - -struct SingleParserTimestampValueDecoder : public ValueDecoder { - using value_type = int64_t; - - explicit SingleParserTimestampValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : ValueDecoder(type, options), - unit_(checked_cast<const TimestampType&>(*type_).unit()), - parser_(*options_.timestamp_parsers[0]) {} - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - if (ARROW_PREDICT_FALSE( - !parser_(reinterpret_cast<const char*>(data), size, unit_, out))) { - return GenericConversionError(type_, data, size); - } - return Status::OK(); - } - - protected: - TimeUnit::type unit_; - const TimestampParser& parser_; -}; - -struct MultipleParsersTimestampValueDecoder : public ValueDecoder { - using value_type = int64_t; - - explicit MultipleParsersTimestampValueDecoder(const std::shared_ptr<DataType>& type, - const ConvertOptions& options) - : ValueDecoder(type, options), - unit_(checked_cast<const TimestampType&>(*type_).unit()), - parsers_(GetParsers(options_)) {} - - Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { - for (const auto& parser : parsers_) { - if (parser->operator()(reinterpret_cast<const char*>(data), size, unit_, out)) { - return Status::OK(); - } - } - return GenericConversionError(type_, data, size); - } - - protected: - using ParserVector = std::vector<const TimestampParser*>; - - static ParserVector GetParsers(const ConvertOptions& options) { - ParserVector parsers(options.timestamp_parsers.size()); - for (size_t i = 0; i < options.timestamp_parsers.size(); ++i) { - parsers[i] = options.timestamp_parsers[i].get(); - } - return parsers; - } - - TimeUnit::type unit_; - std::vector<const TimestampParser*> parsers_; -}; - -///////////////////////////////////////////////////////////////////////// -// Concrete Converter hierarchy - -class ConcreteConverter : public Converter { - public: - using Converter::Converter; -}; - -class ConcreteDictionaryConverter : public DictionaryConverter { - public: - using DictionaryConverter::DictionaryConverter; -}; - -// -// Concrete Converter for nulls -// - -class NullConverter : public ConcreteConverter { - public: - NullConverter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool) - : ConcreteConverter(type, options, pool), decoder_(type_, options_) {} - - Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, - int32_t col_index) override { - NullBuilder builder(pool_); - - auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { - if (ARROW_PREDICT_TRUE(decoder_.IsNull(data, size, quoted))) { - return builder.AppendNull(); - } else { - return GenericConversionError(type_, data, size); - } - }; - RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); - std::shared_ptr<Array> res; - RETURN_NOT_OK(builder.Finish(&res)); - return res; - } - - protected: - Status Initialize() override { return decoder_.Initialize(); } - - ValueDecoder decoder_; -}; - -// -// Concrete Converter for primitives -// - -template <typename T, typename ValueDecoderType> -class PrimitiveConverter : public ConcreteConverter { - public: - PrimitiveConverter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool) - : ConcreteConverter(type, options, pool), decoder_(type_, options_) {} - - Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, - int32_t col_index) override { - using BuilderType = typename TypeTraits<T>::BuilderType; - using value_type = typename ValueDecoderType::value_type; - - BuilderType builder(type_, pool_); - RETURN_NOT_OK(PresizeBuilder(parser, &builder)); - - auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { - if (decoder_.IsNull(data, size, quoted /* quoted */)) { - return builder.AppendNull(); - } - value_type value{}; - RETURN_NOT_OK(decoder_.Decode(data, size, quoted, &value)); - builder.UnsafeAppend(value); - return Status::OK(); - }; - RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); - - std::shared_ptr<Array> res; - RETURN_NOT_OK(builder.Finish(&res)); - return res; - } - - protected: - Status Initialize() override { return decoder_.Initialize(); } - - ValueDecoderType decoder_; -}; - -// -// Concrete Converter for dictionaries -// - -template <typename T, typename ValueDecoderType> -class TypedDictionaryConverter : public ConcreteDictionaryConverter { - public: - TypedDictionaryConverter(const std::shared_ptr<DataType>& value_type, - const ConvertOptions& options, MemoryPool* pool) - : ConcreteDictionaryConverter(value_type, options, pool), - decoder_(value_type, options_) {} - - Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, - int32_t col_index) override { - // We use a fixed index width so that all column chunks get the same index type - using BuilderType = Dictionary32Builder<T>; - using value_type = typename ValueDecoderType::value_type; - - BuilderType builder(value_type_, pool_); - RETURN_NOT_OK(PresizeBuilder(parser, &builder)); - - auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { - if (decoder_.IsNull(data, size, quoted /* quoted */)) { - return builder.AppendNull(); - } - if (ARROW_PREDICT_FALSE(builder.dictionary_length() > max_cardinality_)) { - return Status::IndexError("Dictionary length exceeded max cardinality"); - } - value_type value{}; - RETURN_NOT_OK(decoder_.Decode(data, size, quoted, &value)); - return builder.Append(value); - }; - RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); - - std::shared_ptr<Array> res; - RETURN_NOT_OK(builder.Finish(&res)); - return res; - } - - void SetMaxCardinality(int32_t max_length) override { max_cardinality_ = max_length; } - - protected: - Status Initialize() override { - util::InitializeUTF8(); - return decoder_.Initialize(); - } - - ValueDecoderType decoder_; - int32_t max_cardinality_ = std::numeric_limits<int32_t>::max(); -}; - -// -// Concrete Converter factory for timestamps -// - -template <template <typename, typename> class ConverterType> -std::shared_ptr<Converter> MakeTimestampConverter(const std::shared_ptr<DataType>& type, - const ConvertOptions& options, - MemoryPool* pool) { - if (options.timestamp_parsers.size() == 0) { - // Default to ISO-8601 - return std::make_shared<ConverterType<TimestampType, InlineISO8601ValueDecoder>>( - type, options, pool); - } else if (options.timestamp_parsers.size() == 1) { - // Single user-supplied converter - return std::make_shared< - ConverterType<TimestampType, SingleParserTimestampValueDecoder>>(type, options, - pool); - } else { - // Multiple converters, must iterate for each value - return std::make_shared< - ConverterType<TimestampType, MultipleParsersTimestampValueDecoder>>(type, options, - pool); - } -} - -} // namespace - -///////////////////////////////////////////////////////////////////////// -// Base Converter class implementation - -Converter::Converter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool) - : options_(options), pool_(pool), type_(type) {} - -DictionaryConverter::DictionaryConverter(const std::shared_ptr<DataType>& value_type, - const ConvertOptions& options, MemoryPool* pool) - : Converter(dictionary(int32(), value_type), options, pool), - value_type_(value_type) {} - -Result<std::shared_ptr<Converter>> Converter::Make(const std::shared_ptr<DataType>& type, - const ConvertOptions& options, - MemoryPool* pool) { - std::shared_ptr<Converter> ptr; - - switch (type->id()) { -#define CONVERTER_CASE(TYPE_ID, CONVERTER_TYPE) \ - case TYPE_ID: \ - ptr.reset(new CONVERTER_TYPE(type, options, pool)); \ - break; - -#define NUMERIC_CONVERTER_CASE(TYPE_ID, TYPE_CLASS) \ - CONVERTER_CASE(TYPE_ID, \ - (PrimitiveConverter<TYPE_CLASS, NumericValueDecoder<TYPE_CLASS>>)) - - CONVERTER_CASE(Type::NA, NullConverter) - NUMERIC_CONVERTER_CASE(Type::INT8, Int8Type) - NUMERIC_CONVERTER_CASE(Type::INT16, Int16Type) - NUMERIC_CONVERTER_CASE(Type::INT32, Int32Type) - NUMERIC_CONVERTER_CASE(Type::INT64, Int64Type) - NUMERIC_CONVERTER_CASE(Type::UINT8, UInt8Type) - NUMERIC_CONVERTER_CASE(Type::UINT16, UInt16Type) - NUMERIC_CONVERTER_CASE(Type::UINT32, UInt32Type) - NUMERIC_CONVERTER_CASE(Type::UINT64, UInt64Type) - NUMERIC_CONVERTER_CASE(Type::FLOAT, FloatType) - NUMERIC_CONVERTER_CASE(Type::DOUBLE, DoubleType) - NUMERIC_CONVERTER_CASE(Type::DATE32, Date32Type) - NUMERIC_CONVERTER_CASE(Type::DATE64, Date64Type) - CONVERTER_CASE(Type::BOOL, (PrimitiveConverter<BooleanType, BooleanValueDecoder>)) - CONVERTER_CASE(Type::BINARY, - (PrimitiveConverter<BinaryType, BinaryValueDecoder<false>>)) - CONVERTER_CASE(Type::LARGE_BINARY, - (PrimitiveConverter<LargeBinaryType, BinaryValueDecoder<false>>)) - CONVERTER_CASE(Type::FIXED_SIZE_BINARY, - (PrimitiveConverter<FixedSizeBinaryType, FixedSizeBinaryValueDecoder>)) - CONVERTER_CASE(Type::DECIMAL, - (PrimitiveConverter<Decimal128Type, DecimalValueDecoder>)) - - case Type::TIMESTAMP: - ptr = MakeTimestampConverter<PrimitiveConverter>(type, options, pool); - break; - - case Type::STRING: - if (options.check_utf8) { - ptr = std::make_shared<PrimitiveConverter<StringType, BinaryValueDecoder<true>>>( - type, options, pool); - } else { - ptr = std::make_shared<PrimitiveConverter<StringType, BinaryValueDecoder<false>>>( - type, options, pool); - } - break; - - case Type::LARGE_STRING: - if (options.check_utf8) { - ptr = std::make_shared< - PrimitiveConverter<LargeStringType, BinaryValueDecoder<true>>>(type, options, - pool); - } else { - ptr = std::make_shared< - PrimitiveConverter<LargeStringType, BinaryValueDecoder<false>>>(type, options, - pool); - } - break; - - case Type::DICTIONARY: { - const auto& dict_type = checked_cast<const DictionaryType&>(*type); - if (dict_type.index_type()->id() != Type::INT32) { - return Status::NotImplemented( - "CSV conversion to dictionary only supported for int32 indices, " - "got ", - type->ToString()); - } - return DictionaryConverter::Make(dict_type.value_type(), options, pool); - } - - default: { - return Status::NotImplemented("CSV conversion to ", type->ToString(), - " is not supported"); - } - -#undef CONVERTER_CASE -#undef NUMERIC_CONVERTER_CASE - } - RETURN_NOT_OK(ptr->Initialize()); - return ptr; -} - -Result<std::shared_ptr<DictionaryConverter>> DictionaryConverter::Make( - const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool) { - std::shared_ptr<DictionaryConverter> ptr; - - switch (type->id()) { -#define CONVERTER_CASE(TYPE_ID, TYPE, VALUE_DECODER_TYPE) \ - case TYPE_ID: \ - ptr.reset( \ - new TypedDictionaryConverter<TYPE, VALUE_DECODER_TYPE>(type, options, pool)); \ - break; - - // XXX Are 32-bit types useful? - CONVERTER_CASE(Type::INT32, Int32Type, NumericValueDecoder<Int32Type>) - CONVERTER_CASE(Type::INT64, Int64Type, NumericValueDecoder<Int64Type>) - CONVERTER_CASE(Type::UINT32, UInt32Type, NumericValueDecoder<UInt32Type>) - CONVERTER_CASE(Type::UINT64, UInt64Type, NumericValueDecoder<UInt64Type>) - CONVERTER_CASE(Type::FLOAT, FloatType, NumericValueDecoder<FloatType>) - CONVERTER_CASE(Type::DOUBLE, DoubleType, NumericValueDecoder<DoubleType>) - CONVERTER_CASE(Type::DECIMAL, Decimal128Type, DecimalValueDecoder) - CONVERTER_CASE(Type::FIXED_SIZE_BINARY, FixedSizeBinaryType, - FixedSizeBinaryValueDecoder) - CONVERTER_CASE(Type::BINARY, BinaryType, BinaryValueDecoder<false>) - CONVERTER_CASE(Type::LARGE_BINARY, LargeBinaryType, BinaryValueDecoder<false>) - - case Type::STRING: - if (options.check_utf8) { - ptr = std::make_shared< - TypedDictionaryConverter<StringType, BinaryValueDecoder<true>>>(type, options, - pool); - } else { - ptr = std::make_shared< - TypedDictionaryConverter<StringType, BinaryValueDecoder<false>>>( - type, options, pool); - } - break; - - case Type::LARGE_STRING: - if (options.check_utf8) { - ptr = std::make_shared< - TypedDictionaryConverter<LargeStringType, BinaryValueDecoder<true>>>( - type, options, pool); - } else { - ptr = std::make_shared< - TypedDictionaryConverter<LargeStringType, BinaryValueDecoder<false>>>( - type, options, pool); - } - break; - - default: { - return Status::NotImplemented("CSV dictionary conversion to ", type->ToString(), - " is not supported"); - } - -#undef CONVERTER_CASE - } - RETURN_NOT_OK(ptr->Initialize()); - return ptr; -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/converter.h" + +#include <cstring> +#include <limits> +#include <sstream> +#include <string> +#include <type_traits> +#include <vector> + +#include "arrow/array/builder_binary.h" +#include "arrow/array/builder_decimal.h" +#include "arrow/array/builder_dict.h" +#include "arrow/array/builder_primitive.h" +#include "arrow/csv/parser.h" +#include "arrow/status.h" +#include "arrow/type.h" +#include "arrow/type_fwd.h" +#include "arrow/type_traits.h" +#include "arrow/util/checked_cast.h" +#include "arrow/util/decimal.h" +#include "arrow/util/trie.h" +#include "arrow/util/utf8.h" +#include "arrow/util/value_parsing.h" // IWYU pragma: keep + +namespace arrow { +namespace csv { + +using internal::checked_cast; +using internal::Trie; +using internal::TrieBuilder; + +namespace { + +Status GenericConversionError(const std::shared_ptr<DataType>& type, const uint8_t* data, + uint32_t size) { + return Status::Invalid("CSV conversion error to ", type->ToString(), + ": invalid value '", + std::string(reinterpret_cast<const char*>(data), size), "'"); +} + +inline bool IsWhitespace(uint8_t c) { + if (ARROW_PREDICT_TRUE(c > ' ')) { + return false; + } + return c == ' ' || c == '\t'; +} + +// Updates data_inout and size_inout to not include leading/trailing whitespace +// characters. +inline void TrimWhiteSpace(const uint8_t** data_inout, uint32_t* size_inout) { + const uint8_t*& data = *data_inout; + uint32_t& size = *size_inout; + // Skip trailing whitespace + if (ARROW_PREDICT_TRUE(size > 0) && ARROW_PREDICT_FALSE(IsWhitespace(data[size - 1]))) { + const uint8_t* p = data + size - 1; + while (size > 0 && IsWhitespace(*p)) { + --size; + --p; + } + } + // Skip leading whitespace + if (ARROW_PREDICT_TRUE(size > 0) && ARROW_PREDICT_FALSE(IsWhitespace(data[0]))) { + while (size > 0 && IsWhitespace(*data)) { + --size; + ++data; + } + } +} + +Status InitializeTrie(const std::vector<std::string>& inputs, Trie* trie) { + TrieBuilder builder; + for (const auto& s : inputs) { + RETURN_NOT_OK(builder.Append(s, true /* allow_duplicates */)); + } + *trie = builder.Finish(); + return Status::OK(); +} + +// Presize a builder based on parser contents +template <typename BuilderType> +enable_if_t<!is_base_binary_type<typename BuilderType::TypeClass>::value, Status> +PresizeBuilder(const BlockParser& parser, BuilderType* builder) { + return builder->Resize(parser.num_rows()); +} + +// Same, for variable-sized binary builders +template <typename T> +Status PresizeBuilder(const BlockParser& parser, BaseBinaryBuilder<T>* builder) { + RETURN_NOT_OK(builder->Resize(parser.num_rows())); + return builder->ReserveData(parser.num_bytes()); +} + +///////////////////////////////////////////////////////////////////////// +// Per-type value decoders + +struct ValueDecoder { + explicit ValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : type_(type), options_(options) {} + + Status Initialize() { + // TODO no need to build a separate Trie for each instance + return InitializeTrie(options_.null_values, &null_trie_); + } + + bool IsNull(const uint8_t* data, uint32_t size, bool quoted) { + if (quoted) { + return false; + } + return null_trie_.Find( + util::string_view(reinterpret_cast<const char*>(data), size)) >= 0; + } + + protected: + Trie null_trie_; + std::shared_ptr<DataType> type_; + const ConvertOptions& options_; +}; + +// +// Value decoder for fixed-size binary +// + +struct FixedSizeBinaryValueDecoder : public ValueDecoder { + using value_type = const uint8_t*; + + explicit FixedSizeBinaryValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : ValueDecoder(type, options), + byte_width_(checked_cast<const FixedSizeBinaryType&>(*type).byte_width()) {} + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + if (ARROW_PREDICT_FALSE(size != byte_width_)) { + return Status::Invalid("CSV conversion error to ", type_->ToString(), ": got a ", + size, "-byte long string"); + } + *out = data; + return Status::OK(); + } + + protected: + const uint32_t byte_width_; +}; + +// +// Value decoder for variable-size binary +// + +template <bool CheckUTF8> +struct BinaryValueDecoder : public ValueDecoder { + using value_type = util::string_view; + + using ValueDecoder::ValueDecoder; + + Status Initialize() { + util::InitializeUTF8(); + return ValueDecoder::Initialize(); + } + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + if (CheckUTF8 && ARROW_PREDICT_FALSE(!util::ValidateUTF8(data, size))) { + return Status::Invalid("CSV conversion error to ", type_->ToString(), + ": invalid UTF8 data"); + } + *out = {reinterpret_cast<const char*>(data), size}; + return Status::OK(); + } + + bool IsNull(const uint8_t* data, uint32_t size, bool quoted) { + return options_.strings_can_be_null && + (!quoted || options_.quoted_strings_can_be_null) && + ValueDecoder::IsNull(data, size, false /* quoted */); + } +}; + +// +// Value decoder for integers and floats +// + +template <typename T> +struct NumericValueDecoder : public ValueDecoder { + using value_type = typename T::c_type; + + using ValueDecoder::ValueDecoder; + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + // XXX should quoted values be allowed at all? + TrimWhiteSpace(&data, &size); + if (ARROW_PREDICT_FALSE( + !internal::ParseValue<T>(reinterpret_cast<const char*>(data), size, out))) { + return GenericConversionError(type_, data, size); + } + return Status::OK(); + } +}; + +// +// Value decoder for booleans +// + +struct BooleanValueDecoder : public ValueDecoder { + using value_type = bool; + + using ValueDecoder::ValueDecoder; + + Status Initialize() { + // TODO no need to build separate Tries for each instance + RETURN_NOT_OK(InitializeTrie(options_.true_values, &true_trie_)); + RETURN_NOT_OK(InitializeTrie(options_.false_values, &false_trie_)); + return ValueDecoder::Initialize(); + } + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + // XXX should quoted values be allowed at all? + if (false_trie_.Find(util::string_view(reinterpret_cast<const char*>(data), size)) >= + 0) { + *out = false; + return Status::OK(); + } + if (ARROW_PREDICT_TRUE(true_trie_.Find(util::string_view( + reinterpret_cast<const char*>(data), size)) >= 0)) { + *out = true; + return Status::OK(); + } + return GenericConversionError(type_, data, size); + } + + protected: + Trie true_trie_; + Trie false_trie_; +}; + +// +// Value decoder for decimals +// + +struct DecimalValueDecoder : public ValueDecoder { + using value_type = Decimal128; + + explicit DecimalValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : ValueDecoder(type, options), + decimal_type_(internal::checked_cast<const DecimalType&>(*type_)), + type_precision_(decimal_type_.precision()), + type_scale_(decimal_type_.scale()) {} + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + TrimWhiteSpace(&data, &size); + Decimal128 decimal; + int32_t precision, scale; + util::string_view view(reinterpret_cast<const char*>(data), size); + RETURN_NOT_OK(Decimal128::FromString(view, &decimal, &precision, &scale)); + if (precision > type_precision_) { + return Status::Invalid("Error converting '", view, "' to ", type_->ToString(), + ": precision not supported by type."); + } + if (scale != type_scale_) { + ARROW_ASSIGN_OR_RAISE(*out, decimal.Rescale(scale, type_scale_)); + } else { + *out = std::move(decimal); + } + return Status::OK(); + } + + protected: + const DecimalType& decimal_type_; + const int32_t type_precision_; + const int32_t type_scale_; +}; + +// +// Value decoders for timestamps +// + +struct InlineISO8601ValueDecoder : public ValueDecoder { + using value_type = int64_t; + + explicit InlineISO8601ValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : ValueDecoder(type, options), + unit_(checked_cast<const TimestampType&>(*type_).unit()) {} + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + if (ARROW_PREDICT_FALSE(!internal::ParseTimestampISO8601( + reinterpret_cast<const char*>(data), size, unit_, out))) { + return GenericConversionError(type_, data, size); + } + return Status::OK(); + } + + protected: + TimeUnit::type unit_; +}; + +struct SingleParserTimestampValueDecoder : public ValueDecoder { + using value_type = int64_t; + + explicit SingleParserTimestampValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : ValueDecoder(type, options), + unit_(checked_cast<const TimestampType&>(*type_).unit()), + parser_(*options_.timestamp_parsers[0]) {} + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + if (ARROW_PREDICT_FALSE( + !parser_(reinterpret_cast<const char*>(data), size, unit_, out))) { + return GenericConversionError(type_, data, size); + } + return Status::OK(); + } + + protected: + TimeUnit::type unit_; + const TimestampParser& parser_; +}; + +struct MultipleParsersTimestampValueDecoder : public ValueDecoder { + using value_type = int64_t; + + explicit MultipleParsersTimestampValueDecoder(const std::shared_ptr<DataType>& type, + const ConvertOptions& options) + : ValueDecoder(type, options), + unit_(checked_cast<const TimestampType&>(*type_).unit()), + parsers_(GetParsers(options_)) {} + + Status Decode(const uint8_t* data, uint32_t size, bool quoted, value_type* out) { + for (const auto& parser : parsers_) { + if (parser->operator()(reinterpret_cast<const char*>(data), size, unit_, out)) { + return Status::OK(); + } + } + return GenericConversionError(type_, data, size); + } + + protected: + using ParserVector = std::vector<const TimestampParser*>; + + static ParserVector GetParsers(const ConvertOptions& options) { + ParserVector parsers(options.timestamp_parsers.size()); + for (size_t i = 0; i < options.timestamp_parsers.size(); ++i) { + parsers[i] = options.timestamp_parsers[i].get(); + } + return parsers; + } + + TimeUnit::type unit_; + std::vector<const TimestampParser*> parsers_; +}; + +///////////////////////////////////////////////////////////////////////// +// Concrete Converter hierarchy + +class ConcreteConverter : public Converter { + public: + using Converter::Converter; +}; + +class ConcreteDictionaryConverter : public DictionaryConverter { + public: + using DictionaryConverter::DictionaryConverter; +}; + +// +// Concrete Converter for nulls +// + +class NullConverter : public ConcreteConverter { + public: + NullConverter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool) + : ConcreteConverter(type, options, pool), decoder_(type_, options_) {} + + Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, + int32_t col_index) override { + NullBuilder builder(pool_); + + auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { + if (ARROW_PREDICT_TRUE(decoder_.IsNull(data, size, quoted))) { + return builder.AppendNull(); + } else { + return GenericConversionError(type_, data, size); + } + }; + RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); + std::shared_ptr<Array> res; + RETURN_NOT_OK(builder.Finish(&res)); + return res; + } + + protected: + Status Initialize() override { return decoder_.Initialize(); } + + ValueDecoder decoder_; +}; + +// +// Concrete Converter for primitives +// + +template <typename T, typename ValueDecoderType> +class PrimitiveConverter : public ConcreteConverter { + public: + PrimitiveConverter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool) + : ConcreteConverter(type, options, pool), decoder_(type_, options_) {} + + Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, + int32_t col_index) override { + using BuilderType = typename TypeTraits<T>::BuilderType; + using value_type = typename ValueDecoderType::value_type; + + BuilderType builder(type_, pool_); + RETURN_NOT_OK(PresizeBuilder(parser, &builder)); + + auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { + if (decoder_.IsNull(data, size, quoted /* quoted */)) { + return builder.AppendNull(); + } + value_type value{}; + RETURN_NOT_OK(decoder_.Decode(data, size, quoted, &value)); + builder.UnsafeAppend(value); + return Status::OK(); + }; + RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); + + std::shared_ptr<Array> res; + RETURN_NOT_OK(builder.Finish(&res)); + return res; + } + + protected: + Status Initialize() override { return decoder_.Initialize(); } + + ValueDecoderType decoder_; +}; + +// +// Concrete Converter for dictionaries +// + +template <typename T, typename ValueDecoderType> +class TypedDictionaryConverter : public ConcreteDictionaryConverter { + public: + TypedDictionaryConverter(const std::shared_ptr<DataType>& value_type, + const ConvertOptions& options, MemoryPool* pool) + : ConcreteDictionaryConverter(value_type, options, pool), + decoder_(value_type, options_) {} + + Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, + int32_t col_index) override { + // We use a fixed index width so that all column chunks get the same index type + using BuilderType = Dictionary32Builder<T>; + using value_type = typename ValueDecoderType::value_type; + + BuilderType builder(value_type_, pool_); + RETURN_NOT_OK(PresizeBuilder(parser, &builder)); + + auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { + if (decoder_.IsNull(data, size, quoted /* quoted */)) { + return builder.AppendNull(); + } + if (ARROW_PREDICT_FALSE(builder.dictionary_length() > max_cardinality_)) { + return Status::IndexError("Dictionary length exceeded max cardinality"); + } + value_type value{}; + RETURN_NOT_OK(decoder_.Decode(data, size, quoted, &value)); + return builder.Append(value); + }; + RETURN_NOT_OK(parser.VisitColumn(col_index, visit)); + + std::shared_ptr<Array> res; + RETURN_NOT_OK(builder.Finish(&res)); + return res; + } + + void SetMaxCardinality(int32_t max_length) override { max_cardinality_ = max_length; } + + protected: + Status Initialize() override { + util::InitializeUTF8(); + return decoder_.Initialize(); + } + + ValueDecoderType decoder_; + int32_t max_cardinality_ = std::numeric_limits<int32_t>::max(); +}; + +// +// Concrete Converter factory for timestamps +// + +template <template <typename, typename> class ConverterType> +std::shared_ptr<Converter> MakeTimestampConverter(const std::shared_ptr<DataType>& type, + const ConvertOptions& options, + MemoryPool* pool) { + if (options.timestamp_parsers.size() == 0) { + // Default to ISO-8601 + return std::make_shared<ConverterType<TimestampType, InlineISO8601ValueDecoder>>( + type, options, pool); + } else if (options.timestamp_parsers.size() == 1) { + // Single user-supplied converter + return std::make_shared< + ConverterType<TimestampType, SingleParserTimestampValueDecoder>>(type, options, + pool); + } else { + // Multiple converters, must iterate for each value + return std::make_shared< + ConverterType<TimestampType, MultipleParsersTimestampValueDecoder>>(type, options, + pool); + } +} + +} // namespace + +///////////////////////////////////////////////////////////////////////// +// Base Converter class implementation + +Converter::Converter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool) + : options_(options), pool_(pool), type_(type) {} + +DictionaryConverter::DictionaryConverter(const std::shared_ptr<DataType>& value_type, + const ConvertOptions& options, MemoryPool* pool) + : Converter(dictionary(int32(), value_type), options, pool), + value_type_(value_type) {} + +Result<std::shared_ptr<Converter>> Converter::Make(const std::shared_ptr<DataType>& type, + const ConvertOptions& options, + MemoryPool* pool) { + std::shared_ptr<Converter> ptr; + + switch (type->id()) { +#define CONVERTER_CASE(TYPE_ID, CONVERTER_TYPE) \ + case TYPE_ID: \ + ptr.reset(new CONVERTER_TYPE(type, options, pool)); \ + break; + +#define NUMERIC_CONVERTER_CASE(TYPE_ID, TYPE_CLASS) \ + CONVERTER_CASE(TYPE_ID, \ + (PrimitiveConverter<TYPE_CLASS, NumericValueDecoder<TYPE_CLASS>>)) + + CONVERTER_CASE(Type::NA, NullConverter) + NUMERIC_CONVERTER_CASE(Type::INT8, Int8Type) + NUMERIC_CONVERTER_CASE(Type::INT16, Int16Type) + NUMERIC_CONVERTER_CASE(Type::INT32, Int32Type) + NUMERIC_CONVERTER_CASE(Type::INT64, Int64Type) + NUMERIC_CONVERTER_CASE(Type::UINT8, UInt8Type) + NUMERIC_CONVERTER_CASE(Type::UINT16, UInt16Type) + NUMERIC_CONVERTER_CASE(Type::UINT32, UInt32Type) + NUMERIC_CONVERTER_CASE(Type::UINT64, UInt64Type) + NUMERIC_CONVERTER_CASE(Type::FLOAT, FloatType) + NUMERIC_CONVERTER_CASE(Type::DOUBLE, DoubleType) + NUMERIC_CONVERTER_CASE(Type::DATE32, Date32Type) + NUMERIC_CONVERTER_CASE(Type::DATE64, Date64Type) + CONVERTER_CASE(Type::BOOL, (PrimitiveConverter<BooleanType, BooleanValueDecoder>)) + CONVERTER_CASE(Type::BINARY, + (PrimitiveConverter<BinaryType, BinaryValueDecoder<false>>)) + CONVERTER_CASE(Type::LARGE_BINARY, + (PrimitiveConverter<LargeBinaryType, BinaryValueDecoder<false>>)) + CONVERTER_CASE(Type::FIXED_SIZE_BINARY, + (PrimitiveConverter<FixedSizeBinaryType, FixedSizeBinaryValueDecoder>)) + CONVERTER_CASE(Type::DECIMAL, + (PrimitiveConverter<Decimal128Type, DecimalValueDecoder>)) + + case Type::TIMESTAMP: + ptr = MakeTimestampConverter<PrimitiveConverter>(type, options, pool); + break; + + case Type::STRING: + if (options.check_utf8) { + ptr = std::make_shared<PrimitiveConverter<StringType, BinaryValueDecoder<true>>>( + type, options, pool); + } else { + ptr = std::make_shared<PrimitiveConverter<StringType, BinaryValueDecoder<false>>>( + type, options, pool); + } + break; + + case Type::LARGE_STRING: + if (options.check_utf8) { + ptr = std::make_shared< + PrimitiveConverter<LargeStringType, BinaryValueDecoder<true>>>(type, options, + pool); + } else { + ptr = std::make_shared< + PrimitiveConverter<LargeStringType, BinaryValueDecoder<false>>>(type, options, + pool); + } + break; + + case Type::DICTIONARY: { + const auto& dict_type = checked_cast<const DictionaryType&>(*type); + if (dict_type.index_type()->id() != Type::INT32) { + return Status::NotImplemented( + "CSV conversion to dictionary only supported for int32 indices, " + "got ", + type->ToString()); + } + return DictionaryConverter::Make(dict_type.value_type(), options, pool); + } + + default: { + return Status::NotImplemented("CSV conversion to ", type->ToString(), + " is not supported"); + } + +#undef CONVERTER_CASE +#undef NUMERIC_CONVERTER_CASE + } + RETURN_NOT_OK(ptr->Initialize()); + return ptr; +} + +Result<std::shared_ptr<DictionaryConverter>> DictionaryConverter::Make( + const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool) { + std::shared_ptr<DictionaryConverter> ptr; + + switch (type->id()) { +#define CONVERTER_CASE(TYPE_ID, TYPE, VALUE_DECODER_TYPE) \ + case TYPE_ID: \ + ptr.reset( \ + new TypedDictionaryConverter<TYPE, VALUE_DECODER_TYPE>(type, options, pool)); \ + break; + + // XXX Are 32-bit types useful? + CONVERTER_CASE(Type::INT32, Int32Type, NumericValueDecoder<Int32Type>) + CONVERTER_CASE(Type::INT64, Int64Type, NumericValueDecoder<Int64Type>) + CONVERTER_CASE(Type::UINT32, UInt32Type, NumericValueDecoder<UInt32Type>) + CONVERTER_CASE(Type::UINT64, UInt64Type, NumericValueDecoder<UInt64Type>) + CONVERTER_CASE(Type::FLOAT, FloatType, NumericValueDecoder<FloatType>) + CONVERTER_CASE(Type::DOUBLE, DoubleType, NumericValueDecoder<DoubleType>) + CONVERTER_CASE(Type::DECIMAL, Decimal128Type, DecimalValueDecoder) + CONVERTER_CASE(Type::FIXED_SIZE_BINARY, FixedSizeBinaryType, + FixedSizeBinaryValueDecoder) + CONVERTER_CASE(Type::BINARY, BinaryType, BinaryValueDecoder<false>) + CONVERTER_CASE(Type::LARGE_BINARY, LargeBinaryType, BinaryValueDecoder<false>) + + case Type::STRING: + if (options.check_utf8) { + ptr = std::make_shared< + TypedDictionaryConverter<StringType, BinaryValueDecoder<true>>>(type, options, + pool); + } else { + ptr = std::make_shared< + TypedDictionaryConverter<StringType, BinaryValueDecoder<false>>>( + type, options, pool); + } + break; + + case Type::LARGE_STRING: + if (options.check_utf8) { + ptr = std::make_shared< + TypedDictionaryConverter<LargeStringType, BinaryValueDecoder<true>>>( + type, options, pool); + } else { + ptr = std::make_shared< + TypedDictionaryConverter<LargeStringType, BinaryValueDecoder<false>>>( + type, options, pool); + } + break; + + default: { + return Status::NotImplemented("CSV dictionary conversion to ", type->ToString(), + " is not supported"); + } + +#undef CONVERTER_CASE + } + RETURN_NOT_OK(ptr->Initialize()); + return ptr; +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.h index 3bf1cb898f..639f692f26 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/converter.h @@ -1,82 +1,82 @@ -// 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 <cstdint> -#include <memory> - -#include "arrow/csv/options.h" -#include "arrow/result.h" -#include "arrow/type_fwd.h" -#include "arrow/util/macros.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace csv { - -class BlockParser; - -class ARROW_EXPORT Converter { - public: - Converter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool); - virtual ~Converter() = default; - - virtual Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, - int32_t col_index) = 0; - - std::shared_ptr<DataType> type() const { return type_; } - - // Create a Converter for the given data type - static Result<std::shared_ptr<Converter>> Make( - const std::shared_ptr<DataType>& type, const ConvertOptions& options, - MemoryPool* pool = default_memory_pool()); - - protected: - ARROW_DISALLOW_COPY_AND_ASSIGN(Converter); - - virtual Status Initialize() = 0; - - // CAUTION: ConvertOptions can grow large (if it customizes hundreds or - // thousands of columns), so avoid copying it in each Converter. - const ConvertOptions& options_; - MemoryPool* pool_; - std::shared_ptr<DataType> type_; -}; - -class ARROW_EXPORT DictionaryConverter : public Converter { - public: - DictionaryConverter(const std::shared_ptr<DataType>& value_type, - const ConvertOptions& options, MemoryPool* pool); - - // If the dictionary length goes above this value, conversion will fail - // with Status::IndexError. - virtual void SetMaxCardinality(int32_t max_length) = 0; - - // Create a Converter for the given dictionary value type. - // The dictionary index type will always be Int32. - static Result<std::shared_ptr<DictionaryConverter>> Make( - const std::shared_ptr<DataType>& value_type, const ConvertOptions& options, - MemoryPool* pool = default_memory_pool()); - - protected: - std::shared_ptr<DataType> value_type_; -}; - -} // namespace csv -} // namespace arrow +// 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 <cstdint> +#include <memory> + +#include "arrow/csv/options.h" +#include "arrow/result.h" +#include "arrow/type_fwd.h" +#include "arrow/util/macros.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace csv { + +class BlockParser; + +class ARROW_EXPORT Converter { + public: + Converter(const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool); + virtual ~Converter() = default; + + virtual Result<std::shared_ptr<Array>> Convert(const BlockParser& parser, + int32_t col_index) = 0; + + std::shared_ptr<DataType> type() const { return type_; } + + // Create a Converter for the given data type + static Result<std::shared_ptr<Converter>> Make( + const std::shared_ptr<DataType>& type, const ConvertOptions& options, + MemoryPool* pool = default_memory_pool()); + + protected: + ARROW_DISALLOW_COPY_AND_ASSIGN(Converter); + + virtual Status Initialize() = 0; + + // CAUTION: ConvertOptions can grow large (if it customizes hundreds or + // thousands of columns), so avoid copying it in each Converter. + const ConvertOptions& options_; + MemoryPool* pool_; + std::shared_ptr<DataType> type_; +}; + +class ARROW_EXPORT DictionaryConverter : public Converter { + public: + DictionaryConverter(const std::shared_ptr<DataType>& value_type, + const ConvertOptions& options, MemoryPool* pool); + + // If the dictionary length goes above this value, conversion will fail + // with Status::IndexError. + virtual void SetMaxCardinality(int32_t max_length) = 0; + + // Create a Converter for the given dictionary value type. + // The dictionary index type will always be Int32. + static Result<std::shared_ptr<DictionaryConverter>> Make( + const std::shared_ptr<DataType>& value_type, const ConvertOptions& options, + MemoryPool* pool = default_memory_pool()); + + protected: + std::shared_ptr<DataType> value_type_; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/inference_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/inference_internal.h index 9549a55bea..42486a1eba 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/inference_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/inference_internal.h @@ -1,150 +1,150 @@ -// 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 <memory> - -#include "arrow/csv/converter.h" -#include "arrow/csv/options.h" -#include "arrow/util/logging.h" - -namespace arrow { -namespace csv { - -enum class InferKind { - Null, - Integer, - Boolean, - Real, - Date, - Timestamp, - TimestampNS, - TextDict, - BinaryDict, - Text, - Binary -}; - -class InferStatus { - public: - explicit InferStatus(const ConvertOptions& options) - : kind_(InferKind::Null), can_loosen_type_(true), options_(options) {} - - InferKind kind() const { return kind_; } - - bool can_loosen_type() const { return can_loosen_type_; } - - void LoosenType(const Status& conversion_error) { - DCHECK(can_loosen_type_); - - switch (kind_) { - case InferKind::Null: - return SetKind(InferKind::Integer); - case InferKind::Integer: - return SetKind(InferKind::Boolean); - case InferKind::Boolean: - return SetKind(InferKind::Date); - case InferKind::Date: - return SetKind(InferKind::Timestamp); - case InferKind::Timestamp: - return SetKind(InferKind::TimestampNS); - case InferKind::TimestampNS: - return SetKind(InferKind::Real); - case InferKind::Real: - if (options_.auto_dict_encode) { - return SetKind(InferKind::TextDict); - } else { - return SetKind(InferKind::Text); - } - case InferKind::TextDict: - if (conversion_error.IsIndexError()) { - // Cardinality too large, fall back to non-dict encoding - return SetKind(InferKind::Text); - } else { - // Assuming UTF8 validation failure - return SetKind(InferKind::BinaryDict); - } - break; - case InferKind::BinaryDict: - // Assuming cardinality too large - return SetKind(InferKind::Binary); - case InferKind::Text: - // Assuming UTF8 validation failure - return SetKind(InferKind::Binary); - default: - ARROW_LOG(FATAL) << "Shouldn't come here"; - } - } - - Result<std::shared_ptr<Converter>> MakeConverter(MemoryPool* pool) { - auto make_converter = - [&](std::shared_ptr<DataType> type) -> Result<std::shared_ptr<Converter>> { - return Converter::Make(type, options_, pool); - }; - - auto make_dict_converter = - [&](std::shared_ptr<DataType> type) -> Result<std::shared_ptr<Converter>> { - ARROW_ASSIGN_OR_RAISE(auto dict_converter, - DictionaryConverter::Make(type, options_, pool)); - dict_converter->SetMaxCardinality(options_.auto_dict_max_cardinality); - return dict_converter; - }; - - switch (kind_) { - case InferKind::Null: - return make_converter(null()); - case InferKind::Integer: - return make_converter(int64()); - case InferKind::Boolean: - return make_converter(boolean()); - case InferKind::Date: - return make_converter(date32()); - case InferKind::Timestamp: - return make_converter(timestamp(TimeUnit::SECOND)); - case InferKind::TimestampNS: - return make_converter(timestamp(TimeUnit::NANO)); - case InferKind::Real: - return make_converter(float64()); - case InferKind::Text: - return make_converter(utf8()); - case InferKind::Binary: - return make_converter(binary()); - case InferKind::TextDict: - return make_dict_converter(utf8()); - case InferKind::BinaryDict: - return make_dict_converter(binary()); - } - return Status::UnknownError("Shouldn't come here"); - } - - protected: - void SetKind(InferKind kind) { - kind_ = kind; - if (kind == InferKind::Binary) { - // Binary is the catch-all type - can_loosen_type_ = false; - } - } - - InferKind kind_; - bool can_loosen_type_; - const ConvertOptions& options_; -}; - -} // namespace csv -} // namespace arrow +// 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 <memory> + +#include "arrow/csv/converter.h" +#include "arrow/csv/options.h" +#include "arrow/util/logging.h" + +namespace arrow { +namespace csv { + +enum class InferKind { + Null, + Integer, + Boolean, + Real, + Date, + Timestamp, + TimestampNS, + TextDict, + BinaryDict, + Text, + Binary +}; + +class InferStatus { + public: + explicit InferStatus(const ConvertOptions& options) + : kind_(InferKind::Null), can_loosen_type_(true), options_(options) {} + + InferKind kind() const { return kind_; } + + bool can_loosen_type() const { return can_loosen_type_; } + + void LoosenType(const Status& conversion_error) { + DCHECK(can_loosen_type_); + + switch (kind_) { + case InferKind::Null: + return SetKind(InferKind::Integer); + case InferKind::Integer: + return SetKind(InferKind::Boolean); + case InferKind::Boolean: + return SetKind(InferKind::Date); + case InferKind::Date: + return SetKind(InferKind::Timestamp); + case InferKind::Timestamp: + return SetKind(InferKind::TimestampNS); + case InferKind::TimestampNS: + return SetKind(InferKind::Real); + case InferKind::Real: + if (options_.auto_dict_encode) { + return SetKind(InferKind::TextDict); + } else { + return SetKind(InferKind::Text); + } + case InferKind::TextDict: + if (conversion_error.IsIndexError()) { + // Cardinality too large, fall back to non-dict encoding + return SetKind(InferKind::Text); + } else { + // Assuming UTF8 validation failure + return SetKind(InferKind::BinaryDict); + } + break; + case InferKind::BinaryDict: + // Assuming cardinality too large + return SetKind(InferKind::Binary); + case InferKind::Text: + // Assuming UTF8 validation failure + return SetKind(InferKind::Binary); + default: + ARROW_LOG(FATAL) << "Shouldn't come here"; + } + } + + Result<std::shared_ptr<Converter>> MakeConverter(MemoryPool* pool) { + auto make_converter = + [&](std::shared_ptr<DataType> type) -> Result<std::shared_ptr<Converter>> { + return Converter::Make(type, options_, pool); + }; + + auto make_dict_converter = + [&](std::shared_ptr<DataType> type) -> Result<std::shared_ptr<Converter>> { + ARROW_ASSIGN_OR_RAISE(auto dict_converter, + DictionaryConverter::Make(type, options_, pool)); + dict_converter->SetMaxCardinality(options_.auto_dict_max_cardinality); + return dict_converter; + }; + + switch (kind_) { + case InferKind::Null: + return make_converter(null()); + case InferKind::Integer: + return make_converter(int64()); + case InferKind::Boolean: + return make_converter(boolean()); + case InferKind::Date: + return make_converter(date32()); + case InferKind::Timestamp: + return make_converter(timestamp(TimeUnit::SECOND)); + case InferKind::TimestampNS: + return make_converter(timestamp(TimeUnit::NANO)); + case InferKind::Real: + return make_converter(float64()); + case InferKind::Text: + return make_converter(utf8()); + case InferKind::Binary: + return make_converter(binary()); + case InferKind::TextDict: + return make_dict_converter(utf8()); + case InferKind::BinaryDict: + return make_dict_converter(binary()); + } + return Status::UnknownError("Shouldn't come here"); + } + + protected: + void SetKind(InferKind kind) { + kind_ = kind; + if (kind == InferKind::Binary) { + // Binary is the catch-all type + can_loosen_type_ = false; + } + } + + InferKind kind_; + bool can_loosen_type_; + const ConvertOptions& options_; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.cc index f15fada47e..c71cfdaf29 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.cc @@ -1,83 +1,83 @@ -// 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. - -#include "arrow/csv/options.h" - -namespace arrow { -namespace csv { - -ParseOptions ParseOptions::Defaults() { return ParseOptions(); } - -Status ParseOptions::Validate() const { - if (ARROW_PREDICT_FALSE(delimiter == '\n' || delimiter == '\r')) { - return Status::Invalid("ParseOptions: delimiter cannot be \\r or \\n"); - } - if (ARROW_PREDICT_FALSE(quoting && (quote_char == '\n' || quote_char == '\r'))) { - return Status::Invalid("ParseOptions: quote_char cannot be \\r or \\n"); - } - if (ARROW_PREDICT_FALSE(escaping && (escape_char == '\n' || escape_char == '\r'))) { - return Status::Invalid("ParseOptions: escape_char cannot be \\r or \\n"); - } - return Status::OK(); -} - -ConvertOptions ConvertOptions::Defaults() { - auto options = ConvertOptions(); - // Same default null / true / false spellings as in Pandas. - options.null_values = {"", "#N/A", "#N/A N/A", "#NA", "-1.#IND", "-1.#QNAN", - "-NaN", "-nan", "1.#IND", "1.#QNAN", "N/A", "NA", - "NULL", "NaN", "n/a", "nan", "null"}; - options.true_values = {"1", "True", "TRUE", "true"}; - options.false_values = {"0", "False", "FALSE", "false"}; - return options; -} - -Status ConvertOptions::Validate() const { return Status::OK(); } - -ReadOptions ReadOptions::Defaults() { return ReadOptions(); } - -Status ReadOptions::Validate() const { - if (ARROW_PREDICT_FALSE(block_size < 1)) { - // Min is 1 because some tests use really small block sizes - return Status::Invalid("ReadOptions: block_size must be at least 1: ", block_size); - } - if (ARROW_PREDICT_FALSE(skip_rows < 0)) { - return Status::Invalid("ReadOptions: skip_rows cannot be negative: ", skip_rows); - } - if (ARROW_PREDICT_FALSE(skip_rows_after_names < 0)) { - return Status::Invalid("ReadOptions: skip_rows_after_names cannot be negative: ", - skip_rows_after_names); - } - if (ARROW_PREDICT_FALSE(autogenerate_column_names && !column_names.empty())) { - return Status::Invalid( - "ReadOptions: autogenerate_column_names cannot be true when column_names are " - "provided"); - } - return Status::OK(); -} - -WriteOptions WriteOptions::Defaults() { return WriteOptions(); } - -Status WriteOptions::Validate() const { - if (ARROW_PREDICT_FALSE(batch_size < 1)) { - return Status::Invalid("WriteOptions: batch_size must be at least 1: ", batch_size); - } - return Status::OK(); -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/options.h" + +namespace arrow { +namespace csv { + +ParseOptions ParseOptions::Defaults() { return ParseOptions(); } + +Status ParseOptions::Validate() const { + if (ARROW_PREDICT_FALSE(delimiter == '\n' || delimiter == '\r')) { + return Status::Invalid("ParseOptions: delimiter cannot be \\r or \\n"); + } + if (ARROW_PREDICT_FALSE(quoting && (quote_char == '\n' || quote_char == '\r'))) { + return Status::Invalid("ParseOptions: quote_char cannot be \\r or \\n"); + } + if (ARROW_PREDICT_FALSE(escaping && (escape_char == '\n' || escape_char == '\r'))) { + return Status::Invalid("ParseOptions: escape_char cannot be \\r or \\n"); + } + return Status::OK(); +} + +ConvertOptions ConvertOptions::Defaults() { + auto options = ConvertOptions(); + // Same default null / true / false spellings as in Pandas. + options.null_values = {"", "#N/A", "#N/A N/A", "#NA", "-1.#IND", "-1.#QNAN", + "-NaN", "-nan", "1.#IND", "1.#QNAN", "N/A", "NA", + "NULL", "NaN", "n/a", "nan", "null"}; + options.true_values = {"1", "True", "TRUE", "true"}; + options.false_values = {"0", "False", "FALSE", "false"}; + return options; +} + +Status ConvertOptions::Validate() const { return Status::OK(); } + +ReadOptions ReadOptions::Defaults() { return ReadOptions(); } + +Status ReadOptions::Validate() const { + if (ARROW_PREDICT_FALSE(block_size < 1)) { + // Min is 1 because some tests use really small block sizes + return Status::Invalid("ReadOptions: block_size must be at least 1: ", block_size); + } + if (ARROW_PREDICT_FALSE(skip_rows < 0)) { + return Status::Invalid("ReadOptions: skip_rows cannot be negative: ", skip_rows); + } + if (ARROW_PREDICT_FALSE(skip_rows_after_names < 0)) { + return Status::Invalid("ReadOptions: skip_rows_after_names cannot be negative: ", + skip_rows_after_names); + } + if (ARROW_PREDICT_FALSE(autogenerate_column_names && !column_names.empty())) { + return Status::Invalid( + "ReadOptions: autogenerate_column_names cannot be true when column_names are " + "provided"); + } + return Status::OK(); +} + +WriteOptions WriteOptions::Defaults() { return WriteOptions(); } + +Status WriteOptions::Validate() const { + if (ARROW_PREDICT_FALSE(batch_size < 1)) { + return Status::Invalid("WriteOptions: batch_size must be at least 1: ", batch_size); + } + return Status::OK(); +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.h index 9e6f704af9..5face6f32d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/options.h @@ -1,189 +1,189 @@ -// 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 <cstdint> -#include <memory> -#include <string> -#include <unordered_map> -#include <vector> - -#include "arrow/csv/type_fwd.h" -#include "arrow/io/interfaces.h" -#include "arrow/status.h" -#include "arrow/util/visibility.h" - -namespace arrow { - -class DataType; -class TimestampParser; - -namespace csv { - -// Silly workaround for https://github.com/michaeljones/breathe/issues/453 -constexpr char kDefaultEscapeChar = '\\'; - -struct ARROW_EXPORT ParseOptions { - // Parsing options - - /// Field delimiter - char delimiter = ','; - /// Whether quoting is used - bool quoting = true; - /// Quoting character (if `quoting` is true) - char quote_char = '"'; - /// Whether a quote inside a value is double-quoted - bool double_quote = true; - /// Whether escaping is used - bool escaping = false; - /// Escaping character (if `escaping` is true) - char escape_char = kDefaultEscapeChar; - /// Whether values are allowed to contain CR (0x0d) and LF (0x0a) characters - bool newlines_in_values = false; - /// Whether empty lines are ignored. If false, an empty line represents - /// a single empty value (assuming a one-column CSV file). - bool ignore_empty_lines = true; - - /// Create parsing options with default values - static ParseOptions Defaults(); - - /// \brief Test that all set options are valid - Status Validate() const; -}; - -struct ARROW_EXPORT ConvertOptions { - // Conversion options - - /// Whether to check UTF8 validity of string columns - bool check_utf8 = true; - /// Optional per-column types (disabling type inference on those columns) - std::unordered_map<std::string, std::shared_ptr<DataType>> column_types; - /// Recognized spellings for null values - std::vector<std::string> null_values; - /// Recognized spellings for boolean true values - std::vector<std::string> true_values; - /// Recognized spellings for boolean false values - std::vector<std::string> false_values; - - /// Whether string / binary columns can have null values. - /// - /// If true, then strings in "null_values" are considered null for string columns. - /// If false, then all strings are valid string values. - bool strings_can_be_null = false; - /// Whether string / binary columns can have quoted null values. - /// - /// If true *and* `strings_can_be_null` is true, then quoted strings in - /// "null_values" are also considered null for string columns. Otherwise, - /// quoted strings are never considered null. - bool quoted_strings_can_be_null = true; - - /// Whether to try to automatically dict-encode string / binary data. - /// If true, then when type inference detects a string or binary column, - /// it is dict-encoded up to `auto_dict_max_cardinality` distinct values - /// (per chunk), after which it switches to regular encoding. - /// - /// This setting is ignored for non-inferred columns (those in `column_types`). - bool auto_dict_encode = false; - int32_t auto_dict_max_cardinality = 50; - - // XXX Should we have a separate FilterOptions? - - /// If non-empty, indicates the names of columns from the CSV file that should - /// be actually read and converted (in the vector's order). - /// Columns not in this vector will be ignored. - std::vector<std::string> include_columns; - /// If false, columns in `include_columns` but not in the CSV file will error out. - /// If true, columns in `include_columns` but not in the CSV file will produce - /// a column of nulls (whose type is selected using `column_types`, - /// or null by default) - /// This option is ignored if `include_columns` is empty. - bool include_missing_columns = false; - - /// User-defined timestamp parsers, using the virtual parser interface in - /// arrow/util/value_parsing.h. More than one parser can be specified, and - /// the CSV conversion logic will try parsing values starting from the - /// beginning of this vector. If no parsers are specified, we use the default - /// built-in ISO-8601 parser. - std::vector<std::shared_ptr<TimestampParser>> timestamp_parsers; - - /// Create conversion options with default values, including conventional - /// values for `null_values`, `true_values` and `false_values` - static ConvertOptions Defaults(); - - /// \brief Test that all set options are valid - Status Validate() const; -}; - -struct ARROW_EXPORT ReadOptions { - // Reader options - - /// Whether to use the global CPU thread pool - bool use_threads = true; - - /// \brief Block size we request from the IO layer. - /// - /// This will determine multi-threading granularity as well as - /// the size of individual record batches. - /// Minimum valid value for block size is 1 - int32_t block_size = 1 << 20; // 1 MB - - /// Number of header rows to skip (not including the row of column names, if any) - int32_t skip_rows = 0; - - /// Number of rows to skip after the column names are read, if any - int32_t skip_rows_after_names = 0; - - /// Column names for the target table. - /// If empty, fall back on autogenerate_column_names. - std::vector<std::string> column_names; - - /// Whether to autogenerate column names if `column_names` is empty. - /// If true, column names will be of the form "f0", "f1"... - /// If false, column names will be read from the first CSV row after `skip_rows`. - bool autogenerate_column_names = false; - - /// Create read options with default values - static ReadOptions Defaults(); - - /// \brief Test that all set options are valid - Status Validate() const; -}; - -/// Experimental -struct ARROW_EXPORT WriteOptions { - /// Whether to write an initial header line with column names - bool include_header = true; - - /// \brief Maximum number of rows processed at a time - /// - /// The CSV writer converts and writes data in batches of N rows. - /// This number can impact performance. - int32_t batch_size = 1024; - - /// \brief IO context for writing. - io::IOContext io_context; - - /// Create write options with default values - static WriteOptions Defaults(); - - /// \brief Test that all set options are valid - Status Validate() const; -}; - -} // namespace csv -} // namespace arrow +// 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 <cstdint> +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +#include "arrow/csv/type_fwd.h" +#include "arrow/io/interfaces.h" +#include "arrow/status.h" +#include "arrow/util/visibility.h" + +namespace arrow { + +class DataType; +class TimestampParser; + +namespace csv { + +// Silly workaround for https://github.com/michaeljones/breathe/issues/453 +constexpr char kDefaultEscapeChar = '\\'; + +struct ARROW_EXPORT ParseOptions { + // Parsing options + + /// Field delimiter + char delimiter = ','; + /// Whether quoting is used + bool quoting = true; + /// Quoting character (if `quoting` is true) + char quote_char = '"'; + /// Whether a quote inside a value is double-quoted + bool double_quote = true; + /// Whether escaping is used + bool escaping = false; + /// Escaping character (if `escaping` is true) + char escape_char = kDefaultEscapeChar; + /// Whether values are allowed to contain CR (0x0d) and LF (0x0a) characters + bool newlines_in_values = false; + /// Whether empty lines are ignored. If false, an empty line represents + /// a single empty value (assuming a one-column CSV file). + bool ignore_empty_lines = true; + + /// Create parsing options with default values + static ParseOptions Defaults(); + + /// \brief Test that all set options are valid + Status Validate() const; +}; + +struct ARROW_EXPORT ConvertOptions { + // Conversion options + + /// Whether to check UTF8 validity of string columns + bool check_utf8 = true; + /// Optional per-column types (disabling type inference on those columns) + std::unordered_map<std::string, std::shared_ptr<DataType>> column_types; + /// Recognized spellings for null values + std::vector<std::string> null_values; + /// Recognized spellings for boolean true values + std::vector<std::string> true_values; + /// Recognized spellings for boolean false values + std::vector<std::string> false_values; + + /// Whether string / binary columns can have null values. + /// + /// If true, then strings in "null_values" are considered null for string columns. + /// If false, then all strings are valid string values. + bool strings_can_be_null = false; + /// Whether string / binary columns can have quoted null values. + /// + /// If true *and* `strings_can_be_null` is true, then quoted strings in + /// "null_values" are also considered null for string columns. Otherwise, + /// quoted strings are never considered null. + bool quoted_strings_can_be_null = true; + + /// Whether to try to automatically dict-encode string / binary data. + /// If true, then when type inference detects a string or binary column, + /// it is dict-encoded up to `auto_dict_max_cardinality` distinct values + /// (per chunk), after which it switches to regular encoding. + /// + /// This setting is ignored for non-inferred columns (those in `column_types`). + bool auto_dict_encode = false; + int32_t auto_dict_max_cardinality = 50; + + // XXX Should we have a separate FilterOptions? + + /// If non-empty, indicates the names of columns from the CSV file that should + /// be actually read and converted (in the vector's order). + /// Columns not in this vector will be ignored. + std::vector<std::string> include_columns; + /// If false, columns in `include_columns` but not in the CSV file will error out. + /// If true, columns in `include_columns` but not in the CSV file will produce + /// a column of nulls (whose type is selected using `column_types`, + /// or null by default) + /// This option is ignored if `include_columns` is empty. + bool include_missing_columns = false; + + /// User-defined timestamp parsers, using the virtual parser interface in + /// arrow/util/value_parsing.h. More than one parser can be specified, and + /// the CSV conversion logic will try parsing values starting from the + /// beginning of this vector. If no parsers are specified, we use the default + /// built-in ISO-8601 parser. + std::vector<std::shared_ptr<TimestampParser>> timestamp_parsers; + + /// Create conversion options with default values, including conventional + /// values for `null_values`, `true_values` and `false_values` + static ConvertOptions Defaults(); + + /// \brief Test that all set options are valid + Status Validate() const; +}; + +struct ARROW_EXPORT ReadOptions { + // Reader options + + /// Whether to use the global CPU thread pool + bool use_threads = true; + + /// \brief Block size we request from the IO layer. + /// + /// This will determine multi-threading granularity as well as + /// the size of individual record batches. + /// Minimum valid value for block size is 1 + int32_t block_size = 1 << 20; // 1 MB + + /// Number of header rows to skip (not including the row of column names, if any) + int32_t skip_rows = 0; + + /// Number of rows to skip after the column names are read, if any + int32_t skip_rows_after_names = 0; + + /// Column names for the target table. + /// If empty, fall back on autogenerate_column_names. + std::vector<std::string> column_names; + + /// Whether to autogenerate column names if `column_names` is empty. + /// If true, column names will be of the form "f0", "f1"... + /// If false, column names will be read from the first CSV row after `skip_rows`. + bool autogenerate_column_names = false; + + /// Create read options with default values + static ReadOptions Defaults(); + + /// \brief Test that all set options are valid + Status Validate() const; +}; + +/// Experimental +struct ARROW_EXPORT WriteOptions { + /// Whether to write an initial header line with column names + bool include_header = true; + + /// \brief Maximum number of rows processed at a time + /// + /// The CSV writer converts and writes data in batches of N rows. + /// This number can impact performance. + int32_t batch_size = 1024; + + /// \brief IO context for writing. + io::IOContext io_context; + + /// Create write options with default values + static WriteOptions Defaults(); + + /// \brief Test that all set options are valid + Status Validate() const; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.cc index 0e1fd91c51..446f36a4ee 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.cc @@ -1,581 +1,581 @@ -// 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. - -#include "arrow/csv/parser.h" - -#include <algorithm> -#include <cstdio> -#include <limits> -#include <utility> - -#include "arrow/memory_pool.h" -#include "arrow/result.h" -#include "arrow/status.h" -#include "arrow/util/logging.h" - -namespace arrow { -namespace csv { - -using detail::DataBatch; -using detail::ParsedValueDesc; - -namespace { - -template <typename... Args> -Status ParseError(Args&&... args) { - return Status::Invalid("CSV parse error: ", std::forward<Args>(args)...); -} - -Status MismatchingColumns(int32_t expected, int32_t actual, int64_t row_num, - util::string_view row) { - std::string ellipse; - if (row.length() > 100) { - row = row.substr(0, 96); - ellipse = " ..."; - } - if (row_num < 0) { - return ParseError("Expected ", expected, " columns, got ", actual, ": ", row, - ellipse); - } - return ParseError("Row #", row_num, ": Expected ", expected, " columns, got ", actual, - ": ", row, ellipse); -} - -inline bool IsControlChar(uint8_t c) { return c < ' '; } - -template <bool Quoting, bool Escaping> -class SpecializedOptions { - public: - static constexpr bool quoting = Quoting; - static constexpr bool escaping = Escaping; -}; - -// A helper class allocating the buffer for parsed values and writing into it -// without any further resizes, except at the end. -class PresizedDataWriter { - public: - PresizedDataWriter(MemoryPool* pool, uint32_t size) - : parsed_size_(0), parsed_capacity_(size) { - parsed_buffer_ = *AllocateResizableBuffer(parsed_capacity_, pool); - parsed_ = parsed_buffer_->mutable_data(); - } - - void Finish(std::shared_ptr<Buffer>* out_parsed) { - ARROW_CHECK_OK(parsed_buffer_->Resize(parsed_size_)); - *out_parsed = parsed_buffer_; - } - - void BeginLine() { saved_parsed_size_ = parsed_size_; } - - void PushFieldChar(char c) { - DCHECK_LT(parsed_size_, parsed_capacity_); - parsed_[parsed_size_++] = static_cast<uint8_t>(c); - } - - // Rollback the state that was saved in BeginLine() - void RollbackLine() { parsed_size_ = saved_parsed_size_; } - - int64_t size() { return parsed_size_; } - - protected: - std::shared_ptr<ResizableBuffer> parsed_buffer_; - uint8_t* parsed_; - int64_t parsed_size_; - int64_t parsed_capacity_; - // Checkpointing, for when an incomplete line is encountered at end of block - int64_t saved_parsed_size_; -}; - -template <typename Derived> -class ValueDescWriter { - public: - Derived* derived() { return static_cast<Derived*>(this); } - - template <typename DataWriter> - void Start(DataWriter& parsed_writer) { - derived()->PushValue( - {static_cast<uint32_t>(parsed_writer.size()) & 0x7fffffffU, false}); - } - - void BeginLine() { saved_values_size_ = values_size_; } - - // Rollback the state that was saved in BeginLine() - void RollbackLine() { values_size_ = saved_values_size_; } - - void StartField(bool quoted) { quoted_ = quoted; } - - template <typename DataWriter> - void FinishField(DataWriter* parsed_writer) { - derived()->PushValue( - {static_cast<uint32_t>(parsed_writer->size()) & 0x7fffffffU, quoted_}); - } - - void Finish(std::shared_ptr<Buffer>* out_values) { - ARROW_CHECK_OK(values_buffer_->Resize(values_size_ * sizeof(*values_))); - *out_values = values_buffer_; - } - - protected: - ValueDescWriter(MemoryPool* pool, int64_t values_capacity) - : values_size_(0), values_capacity_(values_capacity) { - values_buffer_ = *AllocateResizableBuffer(values_capacity_ * sizeof(*values_), pool); - values_ = reinterpret_cast<ParsedValueDesc*>(values_buffer_->mutable_data()); - } - - std::shared_ptr<ResizableBuffer> values_buffer_; - ParsedValueDesc* values_; - int64_t values_size_; - int64_t values_capacity_; - bool quoted_; - // Checkpointing, for when an incomplete line is encountered at end of block - int64_t saved_values_size_; -}; - -// A helper class handling a growable buffer for values offsets. This class is -// used when the number of columns is not yet known and we therefore cannot -// efficiently presize the target area for a given number of rows. -class ResizableValueDescWriter : public ValueDescWriter<ResizableValueDescWriter> { - public: - explicit ResizableValueDescWriter(MemoryPool* pool) - : ValueDescWriter(pool, /*values_capacity=*/256) {} - - void PushValue(ParsedValueDesc v) { - if (ARROW_PREDICT_FALSE(values_size_ == values_capacity_)) { - values_capacity_ = values_capacity_ * 2; - ARROW_CHECK_OK(values_buffer_->Resize(values_capacity_ * sizeof(*values_))); - values_ = reinterpret_cast<ParsedValueDesc*>(values_buffer_->mutable_data()); - } - values_[values_size_++] = v; - } -}; - -// A helper class allocating the buffer for values offsets and writing into it -// without any further resizes, except at the end. This class is used once the -// number of columns is known, as it eliminates resizes and generates simpler, -// faster CSV parsing code. -class PresizedValueDescWriter : public ValueDescWriter<PresizedValueDescWriter> { - public: - PresizedValueDescWriter(MemoryPool* pool, int32_t num_rows, int32_t num_cols) - : ValueDescWriter(pool, /*values_capacity=*/1 + num_rows * num_cols) {} - - void PushValue(ParsedValueDesc v) { - DCHECK_LT(values_size_, values_capacity_); - values_[values_size_++] = v; - } -}; - -} // namespace - -class BlockParserImpl { - public: - BlockParserImpl(MemoryPool* pool, ParseOptions options, int32_t num_cols, - int64_t first_row, int32_t max_num_rows) - : pool_(pool), - options_(options), - first_row_(first_row), - max_num_rows_(max_num_rows), - batch_(num_cols) {} - - const DataBatch& parsed_batch() const { return batch_; } - - int64_t first_row_num() const { return first_row_; } - - template <typename SpecializedOptions, typename ValueDescWriter, typename DataWriter> - Status ParseLine(ValueDescWriter* values_writer, DataWriter* parsed_writer, - const char* data, const char* data_end, bool is_final, - const char** out_data) { - int32_t num_cols = 0; - char c; - const auto start = data; - - DCHECK_GT(data_end, data); - - auto FinishField = [&]() { values_writer->FinishField(parsed_writer); }; - - values_writer->BeginLine(); - parsed_writer->BeginLine(); - - // The parsing state machine - - // Special case empty lines: do we start with a newline separator? - c = *data; - if (ARROW_PREDICT_FALSE(IsControlChar(c))) { - if (c == '\r') { - data++; - if (data < data_end && *data == '\n') { - data++; - } - goto EmptyLine; - } - if (c == '\n') { - data++; - goto EmptyLine; - } - } - - FieldStart: - // At the start of a field - // Quoting is only recognized at start of field - if (SpecializedOptions::quoting && - ARROW_PREDICT_FALSE(*data == options_.quote_char)) { - ++data; - values_writer->StartField(true /* quoted */); - goto InQuotedField; - } else { - values_writer->StartField(false /* quoted */); - goto InField; - } - - InField: - // Inside a non-quoted part of a field - if (ARROW_PREDICT_FALSE(data == data_end)) { - goto AbortLine; - } - c = *data++; - if (SpecializedOptions::escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { - if (ARROW_PREDICT_FALSE(data == data_end)) { - goto AbortLine; - } - c = *data++; - parsed_writer->PushFieldChar(c); - goto InField; - } - if (ARROW_PREDICT_FALSE(c == options_.delimiter)) { - goto FieldEnd; - } - if (ARROW_PREDICT_FALSE(IsControlChar(c))) { - if (c == '\r') { - // In the middle of a newline separator? - if (ARROW_PREDICT_TRUE(data < data_end) && *data == '\n') { - data++; - } - goto LineEnd; - } - if (c == '\n') { - goto LineEnd; - } - } - parsed_writer->PushFieldChar(c); - goto InField; - - InQuotedField: - // Inside a quoted part of a field - if (ARROW_PREDICT_FALSE(data == data_end)) { - goto AbortLine; - } - c = *data++; - if (SpecializedOptions::escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { - if (ARROW_PREDICT_FALSE(data == data_end)) { - goto AbortLine; - } - c = *data++; - parsed_writer->PushFieldChar(c); - goto InQuotedField; - } - if (ARROW_PREDICT_FALSE(c == options_.quote_char)) { - if (options_.double_quote && ARROW_PREDICT_TRUE(data < data_end) && - ARROW_PREDICT_FALSE(*data == options_.quote_char)) { - // Double-quoting - ++data; - } else { - // End of single-quoting - goto InField; - } - } - parsed_writer->PushFieldChar(c); - goto InQuotedField; - - FieldEnd: - // At the end of a field - FinishField(); - ++num_cols; - if (ARROW_PREDICT_FALSE(data == data_end)) { - goto AbortLine; - } - goto FieldStart; - - LineEnd: - // At the end of line - FinishField(); - ++num_cols; - if (ARROW_PREDICT_FALSE(num_cols != batch_.num_cols_)) { - if (batch_.num_cols_ == -1) { - batch_.num_cols_ = num_cols; - } else { - // Find the end of the line without newline or carriage return - auto end = data; - if (*(end - 1) == '\n') { - --end; - } - if (*(end - 1) == '\r') { - --end; - } - return MismatchingColumns(batch_.num_cols_, num_cols, - first_row_ < 0 ? -1 : first_row_ + batch_.num_rows_, - util::string_view(start, end - start)); - } - } - ++batch_.num_rows_; - *out_data = data; - return Status::OK(); - - AbortLine: - // Not a full line except perhaps if in final block - if (is_final) { - goto LineEnd; - } - // Truncated line at end of block, rewind parsed state - values_writer->RollbackLine(); - parsed_writer->RollbackLine(); - return Status::OK(); - - EmptyLine: - if (!options_.ignore_empty_lines) { - if (batch_.num_cols_ == -1) { - // Consider as single value - batch_.num_cols_ = 1; - } - // Record as row of empty (null?) values - while (num_cols++ < batch_.num_cols_) { - values_writer->StartField(false /* quoted */); - FinishField(); - } - ++batch_.num_rows_; - } - *out_data = data; - return Status::OK(); - } - - template <typename SpecializedOptions, typename ValueDescWriter, typename DataWriter> - Status ParseChunk(ValueDescWriter* values_writer, DataWriter* parsed_writer, - const char* data, const char* data_end, bool is_final, - int32_t rows_in_chunk, const char** out_data, - bool* finished_parsing) { - int32_t num_rows_deadline = batch_.num_rows_ + rows_in_chunk; - - while (data < data_end && batch_.num_rows_ < num_rows_deadline) { - const char* line_end = data; - RETURN_NOT_OK(ParseLine<SpecializedOptions>(values_writer, parsed_writer, data, - data_end, is_final, &line_end)); - if (line_end == data) { - // Cannot parse any further - *finished_parsing = true; - break; - } - data = line_end; - } - // Append new buffers and update size - std::shared_ptr<Buffer> values_buffer; - values_writer->Finish(&values_buffer); - if (values_buffer->size() > 0) { - values_size_ += - static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc) - 1); - batch_.values_buffers_.push_back(std::move(values_buffer)); - } - *out_data = data; - return Status::OK(); - } - - template <typename SpecializedOptions> - Status ParseSpecialized(const std::vector<util::string_view>& views, bool is_final, - uint32_t* out_size) { - batch_ = DataBatch{batch_.num_cols_}; - values_size_ = 0; - - size_t total_view_length = 0; - for (const auto& view : views) { - total_view_length += view.length(); - } - if (total_view_length > std::numeric_limits<uint32_t>::max()) { - return Status::Invalid("CSV block too large"); - } - - PresizedDataWriter parsed_writer(pool_, static_cast<uint32_t>(total_view_length)); - uint32_t total_parsed_length = 0; - - for (const auto& view : views) { - const char* data = view.data(); - const char* data_end = view.data() + view.length(); - bool finished_parsing = false; - - if (batch_.num_cols_ == -1) { - // Can't presize values when the number of columns is not known, first parse - // a single line - const int32_t rows_in_chunk = 1; - ResizableValueDescWriter values_writer(pool_); - values_writer.Start(parsed_writer); - - RETURN_NOT_OK(ParseChunk<SpecializedOptions>(&values_writer, &parsed_writer, data, - data_end, is_final, rows_in_chunk, - &data, &finished_parsing)); - if (batch_.num_cols_ == -1) { - return ParseError("Empty CSV file or block: cannot infer number of columns"); - } - } - - while (!finished_parsing && data < data_end && batch_.num_rows_ < max_num_rows_) { - // We know the number of columns, so can presize a values array for - // a given number of rows - DCHECK_GE(batch_.num_cols_, 0); - - int32_t rows_in_chunk; - constexpr int32_t kTargetChunkSize = 32768; // in number of values - if (batch_.num_cols_ > 0) { - rows_in_chunk = std::min(std::max(kTargetChunkSize / batch_.num_cols_, 512), - max_num_rows_ - batch_.num_rows_); - } else { - rows_in_chunk = std::min(kTargetChunkSize, max_num_rows_ - batch_.num_rows_); - } - - PresizedValueDescWriter values_writer(pool_, rows_in_chunk, batch_.num_cols_); - values_writer.Start(parsed_writer); - - RETURN_NOT_OK(ParseChunk<SpecializedOptions>(&values_writer, &parsed_writer, data, - data_end, is_final, rows_in_chunk, - &data, &finished_parsing)); - } - DCHECK_GE(data, view.data()); - DCHECK_LE(data, data_end); - total_parsed_length += static_cast<uint32_t>(data - view.data()); - - if (data < data_end) { - // Stopped early, for some reason - break; - } - } - - parsed_writer.Finish(&batch_.parsed_buffer_); - batch_.parsed_size_ = static_cast<int32_t>(batch_.parsed_buffer_->size()); - batch_.parsed_ = batch_.parsed_buffer_->data(); - - if (batch_.num_cols_ == -1) { - DCHECK_EQ(batch_.num_rows_, 0); - } - DCHECK_EQ(values_size_, batch_.num_rows_ * batch_.num_cols_); -#ifndef NDEBUG - if (batch_.num_rows_ > 0) { - // Ending parsed offset should be equal to number of parsed bytes - DCHECK_GT(batch_.values_buffers_.size(), 0); - const auto& last_values_buffer = batch_.values_buffers_.back(); - const auto last_values = - reinterpret_cast<const ParsedValueDesc*>(last_values_buffer->data()); - const auto last_values_size = last_values_buffer->size() / sizeof(ParsedValueDesc); - const auto check_parsed_size = - static_cast<int32_t>(last_values[last_values_size - 1].offset); - DCHECK_EQ(batch_.parsed_size_, check_parsed_size); - } else { - DCHECK_EQ(batch_.parsed_size_, 0); - } -#endif - *out_size = static_cast<uint32_t>(total_parsed_length); - return Status::OK(); - } - - Status Parse(const std::vector<util::string_view>& data, bool is_final, - uint32_t* out_size) { - if (options_.quoting) { - if (options_.escaping) { - return ParseSpecialized<SpecializedOptions<true, true>>(data, is_final, out_size); - } else { - return ParseSpecialized<SpecializedOptions<true, false>>(data, is_final, - out_size); - } - } else { - if (options_.escaping) { - return ParseSpecialized<SpecializedOptions<false, true>>(data, is_final, - out_size); - } else { - return ParseSpecialized<SpecializedOptions<false, false>>(data, is_final, - out_size); - } - } - } - - protected: - MemoryPool* pool_; - const ParseOptions options_; - const int64_t first_row_; - // The maximum number of rows to parse from a block - int32_t max_num_rows_; - - // Unparsed data size - int32_t values_size_; - // Parsed data batch - DataBatch batch_; -}; - -BlockParser::BlockParser(ParseOptions options, int32_t num_cols, int64_t first_row, - int32_t max_num_rows) - : BlockParser(default_memory_pool(), options, num_cols, first_row, max_num_rows) {} - -BlockParser::BlockParser(MemoryPool* pool, ParseOptions options, int32_t num_cols, - int64_t first_row, int32_t max_num_rows) - : impl_(new BlockParserImpl(pool, std::move(options), num_cols, first_row, - max_num_rows)) {} - -BlockParser::~BlockParser() {} - -Status BlockParser::Parse(const std::vector<util::string_view>& data, - uint32_t* out_size) { - return impl_->Parse(data, false /* is_final */, out_size); -} - -Status BlockParser::ParseFinal(const std::vector<util::string_view>& data, - uint32_t* out_size) { - return impl_->Parse(data, true /* is_final */, out_size); -} - -Status BlockParser::Parse(util::string_view data, uint32_t* out_size) { - return impl_->Parse({data}, false /* is_final */, out_size); -} - -Status BlockParser::ParseFinal(util::string_view data, uint32_t* out_size) { - return impl_->Parse({data}, true /* is_final */, out_size); -} - -const DataBatch& BlockParser::parsed_batch() const { return impl_->parsed_batch(); } - -int64_t BlockParser::first_row_num() const { return impl_->first_row_num(); } - -int32_t SkipRows(const uint8_t* data, uint32_t size, int32_t num_rows, - const uint8_t** out_data) { - const auto end = data + size; - int32_t skipped_rows = 0; - *out_data = data; - - for (; skipped_rows < num_rows; ++skipped_rows) { - uint8_t c; - do { - while (ARROW_PREDICT_FALSE(data < end && !IsControlChar(*data))) { - ++data; - } - if (ARROW_PREDICT_FALSE(data == end)) { - return skipped_rows; - } - c = *data++; - } while (c != '\r' && c != '\n'); - if (c == '\r' && data < end && *data == '\n') { - ++data; - } - *out_data = data; - } - - return skipped_rows; -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/parser.h" + +#include <algorithm> +#include <cstdio> +#include <limits> +#include <utility> + +#include "arrow/memory_pool.h" +#include "arrow/result.h" +#include "arrow/status.h" +#include "arrow/util/logging.h" + +namespace arrow { +namespace csv { + +using detail::DataBatch; +using detail::ParsedValueDesc; + +namespace { + +template <typename... Args> +Status ParseError(Args&&... args) { + return Status::Invalid("CSV parse error: ", std::forward<Args>(args)...); +} + +Status MismatchingColumns(int32_t expected, int32_t actual, int64_t row_num, + util::string_view row) { + std::string ellipse; + if (row.length() > 100) { + row = row.substr(0, 96); + ellipse = " ..."; + } + if (row_num < 0) { + return ParseError("Expected ", expected, " columns, got ", actual, ": ", row, + ellipse); + } + return ParseError("Row #", row_num, ": Expected ", expected, " columns, got ", actual, + ": ", row, ellipse); +} + +inline bool IsControlChar(uint8_t c) { return c < ' '; } + +template <bool Quoting, bool Escaping> +class SpecializedOptions { + public: + static constexpr bool quoting = Quoting; + static constexpr bool escaping = Escaping; +}; + +// A helper class allocating the buffer for parsed values and writing into it +// without any further resizes, except at the end. +class PresizedDataWriter { + public: + PresizedDataWriter(MemoryPool* pool, uint32_t size) + : parsed_size_(0), parsed_capacity_(size) { + parsed_buffer_ = *AllocateResizableBuffer(parsed_capacity_, pool); + parsed_ = parsed_buffer_->mutable_data(); + } + + void Finish(std::shared_ptr<Buffer>* out_parsed) { + ARROW_CHECK_OK(parsed_buffer_->Resize(parsed_size_)); + *out_parsed = parsed_buffer_; + } + + void BeginLine() { saved_parsed_size_ = parsed_size_; } + + void PushFieldChar(char c) { + DCHECK_LT(parsed_size_, parsed_capacity_); + parsed_[parsed_size_++] = static_cast<uint8_t>(c); + } + + // Rollback the state that was saved in BeginLine() + void RollbackLine() { parsed_size_ = saved_parsed_size_; } + + int64_t size() { return parsed_size_; } + + protected: + std::shared_ptr<ResizableBuffer> parsed_buffer_; + uint8_t* parsed_; + int64_t parsed_size_; + int64_t parsed_capacity_; + // Checkpointing, for when an incomplete line is encountered at end of block + int64_t saved_parsed_size_; +}; + +template <typename Derived> +class ValueDescWriter { + public: + Derived* derived() { return static_cast<Derived*>(this); } + + template <typename DataWriter> + void Start(DataWriter& parsed_writer) { + derived()->PushValue( + {static_cast<uint32_t>(parsed_writer.size()) & 0x7fffffffU, false}); + } + + void BeginLine() { saved_values_size_ = values_size_; } + + // Rollback the state that was saved in BeginLine() + void RollbackLine() { values_size_ = saved_values_size_; } + + void StartField(bool quoted) { quoted_ = quoted; } + + template <typename DataWriter> + void FinishField(DataWriter* parsed_writer) { + derived()->PushValue( + {static_cast<uint32_t>(parsed_writer->size()) & 0x7fffffffU, quoted_}); + } + + void Finish(std::shared_ptr<Buffer>* out_values) { + ARROW_CHECK_OK(values_buffer_->Resize(values_size_ * sizeof(*values_))); + *out_values = values_buffer_; + } + + protected: + ValueDescWriter(MemoryPool* pool, int64_t values_capacity) + : values_size_(0), values_capacity_(values_capacity) { + values_buffer_ = *AllocateResizableBuffer(values_capacity_ * sizeof(*values_), pool); + values_ = reinterpret_cast<ParsedValueDesc*>(values_buffer_->mutable_data()); + } + + std::shared_ptr<ResizableBuffer> values_buffer_; + ParsedValueDesc* values_; + int64_t values_size_; + int64_t values_capacity_; + bool quoted_; + // Checkpointing, for when an incomplete line is encountered at end of block + int64_t saved_values_size_; +}; + +// A helper class handling a growable buffer for values offsets. This class is +// used when the number of columns is not yet known and we therefore cannot +// efficiently presize the target area for a given number of rows. +class ResizableValueDescWriter : public ValueDescWriter<ResizableValueDescWriter> { + public: + explicit ResizableValueDescWriter(MemoryPool* pool) + : ValueDescWriter(pool, /*values_capacity=*/256) {} + + void PushValue(ParsedValueDesc v) { + if (ARROW_PREDICT_FALSE(values_size_ == values_capacity_)) { + values_capacity_ = values_capacity_ * 2; + ARROW_CHECK_OK(values_buffer_->Resize(values_capacity_ * sizeof(*values_))); + values_ = reinterpret_cast<ParsedValueDesc*>(values_buffer_->mutable_data()); + } + values_[values_size_++] = v; + } +}; + +// A helper class allocating the buffer for values offsets and writing into it +// without any further resizes, except at the end. This class is used once the +// number of columns is known, as it eliminates resizes and generates simpler, +// faster CSV parsing code. +class PresizedValueDescWriter : public ValueDescWriter<PresizedValueDescWriter> { + public: + PresizedValueDescWriter(MemoryPool* pool, int32_t num_rows, int32_t num_cols) + : ValueDescWriter(pool, /*values_capacity=*/1 + num_rows * num_cols) {} + + void PushValue(ParsedValueDesc v) { + DCHECK_LT(values_size_, values_capacity_); + values_[values_size_++] = v; + } +}; + +} // namespace + +class BlockParserImpl { + public: + BlockParserImpl(MemoryPool* pool, ParseOptions options, int32_t num_cols, + int64_t first_row, int32_t max_num_rows) + : pool_(pool), + options_(options), + first_row_(first_row), + max_num_rows_(max_num_rows), + batch_(num_cols) {} + + const DataBatch& parsed_batch() const { return batch_; } + + int64_t first_row_num() const { return first_row_; } + + template <typename SpecializedOptions, typename ValueDescWriter, typename DataWriter> + Status ParseLine(ValueDescWriter* values_writer, DataWriter* parsed_writer, + const char* data, const char* data_end, bool is_final, + const char** out_data) { + int32_t num_cols = 0; + char c; + const auto start = data; + + DCHECK_GT(data_end, data); + + auto FinishField = [&]() { values_writer->FinishField(parsed_writer); }; + + values_writer->BeginLine(); + parsed_writer->BeginLine(); + + // The parsing state machine + + // Special case empty lines: do we start with a newline separator? + c = *data; + if (ARROW_PREDICT_FALSE(IsControlChar(c))) { + if (c == '\r') { + data++; + if (data < data_end && *data == '\n') { + data++; + } + goto EmptyLine; + } + if (c == '\n') { + data++; + goto EmptyLine; + } + } + + FieldStart: + // At the start of a field + // Quoting is only recognized at start of field + if (SpecializedOptions::quoting && + ARROW_PREDICT_FALSE(*data == options_.quote_char)) { + ++data; + values_writer->StartField(true /* quoted */); + goto InQuotedField; + } else { + values_writer->StartField(false /* quoted */); + goto InField; + } + + InField: + // Inside a non-quoted part of a field + if (ARROW_PREDICT_FALSE(data == data_end)) { + goto AbortLine; + } + c = *data++; + if (SpecializedOptions::escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { + if (ARROW_PREDICT_FALSE(data == data_end)) { + goto AbortLine; + } + c = *data++; + parsed_writer->PushFieldChar(c); + goto InField; + } + if (ARROW_PREDICT_FALSE(c == options_.delimiter)) { + goto FieldEnd; + } + if (ARROW_PREDICT_FALSE(IsControlChar(c))) { + if (c == '\r') { + // In the middle of a newline separator? + if (ARROW_PREDICT_TRUE(data < data_end) && *data == '\n') { + data++; + } + goto LineEnd; + } + if (c == '\n') { + goto LineEnd; + } + } + parsed_writer->PushFieldChar(c); + goto InField; + + InQuotedField: + // Inside a quoted part of a field + if (ARROW_PREDICT_FALSE(data == data_end)) { + goto AbortLine; + } + c = *data++; + if (SpecializedOptions::escaping && ARROW_PREDICT_FALSE(c == options_.escape_char)) { + if (ARROW_PREDICT_FALSE(data == data_end)) { + goto AbortLine; + } + c = *data++; + parsed_writer->PushFieldChar(c); + goto InQuotedField; + } + if (ARROW_PREDICT_FALSE(c == options_.quote_char)) { + if (options_.double_quote && ARROW_PREDICT_TRUE(data < data_end) && + ARROW_PREDICT_FALSE(*data == options_.quote_char)) { + // Double-quoting + ++data; + } else { + // End of single-quoting + goto InField; + } + } + parsed_writer->PushFieldChar(c); + goto InQuotedField; + + FieldEnd: + // At the end of a field + FinishField(); + ++num_cols; + if (ARROW_PREDICT_FALSE(data == data_end)) { + goto AbortLine; + } + goto FieldStart; + + LineEnd: + // At the end of line + FinishField(); + ++num_cols; + if (ARROW_PREDICT_FALSE(num_cols != batch_.num_cols_)) { + if (batch_.num_cols_ == -1) { + batch_.num_cols_ = num_cols; + } else { + // Find the end of the line without newline or carriage return + auto end = data; + if (*(end - 1) == '\n') { + --end; + } + if (*(end - 1) == '\r') { + --end; + } + return MismatchingColumns(batch_.num_cols_, num_cols, + first_row_ < 0 ? -1 : first_row_ + batch_.num_rows_, + util::string_view(start, end - start)); + } + } + ++batch_.num_rows_; + *out_data = data; + return Status::OK(); + + AbortLine: + // Not a full line except perhaps if in final block + if (is_final) { + goto LineEnd; + } + // Truncated line at end of block, rewind parsed state + values_writer->RollbackLine(); + parsed_writer->RollbackLine(); + return Status::OK(); + + EmptyLine: + if (!options_.ignore_empty_lines) { + if (batch_.num_cols_ == -1) { + // Consider as single value + batch_.num_cols_ = 1; + } + // Record as row of empty (null?) values + while (num_cols++ < batch_.num_cols_) { + values_writer->StartField(false /* quoted */); + FinishField(); + } + ++batch_.num_rows_; + } + *out_data = data; + return Status::OK(); + } + + template <typename SpecializedOptions, typename ValueDescWriter, typename DataWriter> + Status ParseChunk(ValueDescWriter* values_writer, DataWriter* parsed_writer, + const char* data, const char* data_end, bool is_final, + int32_t rows_in_chunk, const char** out_data, + bool* finished_parsing) { + int32_t num_rows_deadline = batch_.num_rows_ + rows_in_chunk; + + while (data < data_end && batch_.num_rows_ < num_rows_deadline) { + const char* line_end = data; + RETURN_NOT_OK(ParseLine<SpecializedOptions>(values_writer, parsed_writer, data, + data_end, is_final, &line_end)); + if (line_end == data) { + // Cannot parse any further + *finished_parsing = true; + break; + } + data = line_end; + } + // Append new buffers and update size + std::shared_ptr<Buffer> values_buffer; + values_writer->Finish(&values_buffer); + if (values_buffer->size() > 0) { + values_size_ += + static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc) - 1); + batch_.values_buffers_.push_back(std::move(values_buffer)); + } + *out_data = data; + return Status::OK(); + } + + template <typename SpecializedOptions> + Status ParseSpecialized(const std::vector<util::string_view>& views, bool is_final, + uint32_t* out_size) { + batch_ = DataBatch{batch_.num_cols_}; + values_size_ = 0; + + size_t total_view_length = 0; + for (const auto& view : views) { + total_view_length += view.length(); + } + if (total_view_length > std::numeric_limits<uint32_t>::max()) { + return Status::Invalid("CSV block too large"); + } + + PresizedDataWriter parsed_writer(pool_, static_cast<uint32_t>(total_view_length)); + uint32_t total_parsed_length = 0; + + for (const auto& view : views) { + const char* data = view.data(); + const char* data_end = view.data() + view.length(); + bool finished_parsing = false; + + if (batch_.num_cols_ == -1) { + // Can't presize values when the number of columns is not known, first parse + // a single line + const int32_t rows_in_chunk = 1; + ResizableValueDescWriter values_writer(pool_); + values_writer.Start(parsed_writer); + + RETURN_NOT_OK(ParseChunk<SpecializedOptions>(&values_writer, &parsed_writer, data, + data_end, is_final, rows_in_chunk, + &data, &finished_parsing)); + if (batch_.num_cols_ == -1) { + return ParseError("Empty CSV file or block: cannot infer number of columns"); + } + } + + while (!finished_parsing && data < data_end && batch_.num_rows_ < max_num_rows_) { + // We know the number of columns, so can presize a values array for + // a given number of rows + DCHECK_GE(batch_.num_cols_, 0); + + int32_t rows_in_chunk; + constexpr int32_t kTargetChunkSize = 32768; // in number of values + if (batch_.num_cols_ > 0) { + rows_in_chunk = std::min(std::max(kTargetChunkSize / batch_.num_cols_, 512), + max_num_rows_ - batch_.num_rows_); + } else { + rows_in_chunk = std::min(kTargetChunkSize, max_num_rows_ - batch_.num_rows_); + } + + PresizedValueDescWriter values_writer(pool_, rows_in_chunk, batch_.num_cols_); + values_writer.Start(parsed_writer); + + RETURN_NOT_OK(ParseChunk<SpecializedOptions>(&values_writer, &parsed_writer, data, + data_end, is_final, rows_in_chunk, + &data, &finished_parsing)); + } + DCHECK_GE(data, view.data()); + DCHECK_LE(data, data_end); + total_parsed_length += static_cast<uint32_t>(data - view.data()); + + if (data < data_end) { + // Stopped early, for some reason + break; + } + } + + parsed_writer.Finish(&batch_.parsed_buffer_); + batch_.parsed_size_ = static_cast<int32_t>(batch_.parsed_buffer_->size()); + batch_.parsed_ = batch_.parsed_buffer_->data(); + + if (batch_.num_cols_ == -1) { + DCHECK_EQ(batch_.num_rows_, 0); + } + DCHECK_EQ(values_size_, batch_.num_rows_ * batch_.num_cols_); +#ifndef NDEBUG + if (batch_.num_rows_ > 0) { + // Ending parsed offset should be equal to number of parsed bytes + DCHECK_GT(batch_.values_buffers_.size(), 0); + const auto& last_values_buffer = batch_.values_buffers_.back(); + const auto last_values = + reinterpret_cast<const ParsedValueDesc*>(last_values_buffer->data()); + const auto last_values_size = last_values_buffer->size() / sizeof(ParsedValueDesc); + const auto check_parsed_size = + static_cast<int32_t>(last_values[last_values_size - 1].offset); + DCHECK_EQ(batch_.parsed_size_, check_parsed_size); + } else { + DCHECK_EQ(batch_.parsed_size_, 0); + } +#endif + *out_size = static_cast<uint32_t>(total_parsed_length); + return Status::OK(); + } + + Status Parse(const std::vector<util::string_view>& data, bool is_final, + uint32_t* out_size) { + if (options_.quoting) { + if (options_.escaping) { + return ParseSpecialized<SpecializedOptions<true, true>>(data, is_final, out_size); + } else { + return ParseSpecialized<SpecializedOptions<true, false>>(data, is_final, + out_size); + } + } else { + if (options_.escaping) { + return ParseSpecialized<SpecializedOptions<false, true>>(data, is_final, + out_size); + } else { + return ParseSpecialized<SpecializedOptions<false, false>>(data, is_final, + out_size); + } + } + } + + protected: + MemoryPool* pool_; + const ParseOptions options_; + const int64_t first_row_; + // The maximum number of rows to parse from a block + int32_t max_num_rows_; + + // Unparsed data size + int32_t values_size_; + // Parsed data batch + DataBatch batch_; +}; + +BlockParser::BlockParser(ParseOptions options, int32_t num_cols, int64_t first_row, + int32_t max_num_rows) + : BlockParser(default_memory_pool(), options, num_cols, first_row, max_num_rows) {} + +BlockParser::BlockParser(MemoryPool* pool, ParseOptions options, int32_t num_cols, + int64_t first_row, int32_t max_num_rows) + : impl_(new BlockParserImpl(pool, std::move(options), num_cols, first_row, + max_num_rows)) {} + +BlockParser::~BlockParser() {} + +Status BlockParser::Parse(const std::vector<util::string_view>& data, + uint32_t* out_size) { + return impl_->Parse(data, false /* is_final */, out_size); +} + +Status BlockParser::ParseFinal(const std::vector<util::string_view>& data, + uint32_t* out_size) { + return impl_->Parse(data, true /* is_final */, out_size); +} + +Status BlockParser::Parse(util::string_view data, uint32_t* out_size) { + return impl_->Parse({data}, false /* is_final */, out_size); +} + +Status BlockParser::ParseFinal(util::string_view data, uint32_t* out_size) { + return impl_->Parse({data}, true /* is_final */, out_size); +} + +const DataBatch& BlockParser::parsed_batch() const { return impl_->parsed_batch(); } + +int64_t BlockParser::first_row_num() const { return impl_->first_row_num(); } + +int32_t SkipRows(const uint8_t* data, uint32_t size, int32_t num_rows, + const uint8_t** out_data) { + const auto end = data + size; + int32_t skipped_rows = 0; + *out_data = data; + + for (; skipped_rows < num_rows; ++skipped_rows) { + uint8_t c; + do { + while (ARROW_PREDICT_FALSE(data < end && !IsControlChar(*data))) { + ++data; + } + if (ARROW_PREDICT_FALSE(data == end)) { + return skipped_rows; + } + c = *data++; + } while (c != '\r' && c != '\n'); + if (c == '\r' && data < end && *data == '\n') { + ++data; + } + *out_data = data; + } + + return skipped_rows; +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.h index 76ba0fbaf3..ffc735c228 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/parser.h @@ -1,202 +1,202 @@ -// 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 <cstddef> -#include <cstdint> -#include <memory> -#include <vector> - -#include "arrow/buffer.h" -#include "arrow/csv/options.h" -#include "arrow/status.h" -#include "arrow/util/macros.h" -#include "arrow/util/string_view.h" -#include "arrow/util/visibility.h" - -namespace arrow { - -class MemoryPool; - -namespace csv { - -/// Skip at most num_rows from the given input. The input pointer is updated -/// and the number of actually skipped rows is returns (may be less than -/// requested if the input is too short). -ARROW_EXPORT -int32_t SkipRows(const uint8_t* data, uint32_t size, int32_t num_rows, - const uint8_t** out_data); - -class BlockParserImpl; - -namespace detail { - -struct ParsedValueDesc { - uint32_t offset : 31; - bool quoted : 1; -}; - -class ARROW_EXPORT DataBatch { - public: - explicit DataBatch(int32_t num_cols) : num_cols_(num_cols) {} - - /// \brief Return the number of parsed rows - int32_t num_rows() const { return num_rows_; } - /// \brief Return the number of parsed columns - int32_t num_cols() const { return num_cols_; } - /// \brief Return the total size in bytes of parsed data - uint32_t num_bytes() const { return parsed_size_; } - - template <typename Visitor> - Status VisitColumn(int32_t col_index, int64_t first_row, Visitor&& visit) const { - using detail::ParsedValueDesc; - - int64_t row = first_row; - for (size_t buf_index = 0; buf_index < values_buffers_.size(); ++buf_index) { - const auto& values_buffer = values_buffers_[buf_index]; - const auto values = reinterpret_cast<const ParsedValueDesc*>(values_buffer->data()); - const auto max_pos = - static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc)) - 1; - for (int32_t pos = col_index; pos < max_pos; pos += num_cols_, ++row) { - auto start = values[pos].offset; - auto stop = values[pos + 1].offset; - auto quoted = values[pos + 1].quoted; - Status status = visit(parsed_ + start, stop - start, quoted); - if (ARROW_PREDICT_FALSE(!status.ok())) { - if (first_row >= 0) { - status = status.WithMessage("Row #", row, ": ", status.message()); - } - ARROW_RETURN_NOT_OK(status); - } - } - } - return Status::OK(); - } - - template <typename Visitor> - Status VisitLastRow(Visitor&& visit) const { - using detail::ParsedValueDesc; - - const auto& values_buffer = values_buffers_.back(); - const auto values = reinterpret_cast<const ParsedValueDesc*>(values_buffer->data()); - const auto start_pos = - static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc)) - - num_cols_ - 1; - for (int32_t col_index = 0; col_index < num_cols_; ++col_index) { - auto start = values[start_pos + col_index].offset; - auto stop = values[start_pos + col_index + 1].offset; - auto quoted = values[start_pos + col_index + 1].quoted; - ARROW_RETURN_NOT_OK(visit(parsed_ + start, stop - start, quoted)); - } - return Status::OK(); - } - - protected: - // The number of rows in this batch - int32_t num_rows_ = 0; - // The number of columns - int32_t num_cols_ = 0; - - // XXX should we ensure the parsed buffer is padded with 8 or 16 excess zero bytes? - // It may help with null parsing... - std::vector<std::shared_ptr<Buffer>> values_buffers_; - std::shared_ptr<Buffer> parsed_buffer_; - const uint8_t* parsed_ = NULLPTR; - int32_t parsed_size_ = 0; - - friend class ::arrow::csv::BlockParserImpl; -}; - -} // namespace detail - -constexpr int32_t kMaxParserNumRows = 100000; - -/// \class BlockParser -/// \brief A reusable block-based parser for CSV data -/// -/// The parser takes a block of CSV data and delimits rows and fields, -/// unquoting and unescaping them on the fly. Parsed data is own by the -/// parser, so the original buffer can be discarded after Parse() returns. -/// -/// If the block is truncated (i.e. not all data can be parsed), it is up -/// to the caller to arrange the next block to start with the trailing data. -/// Also, if the previous block ends with CR (0x0d) and a new block starts -/// with LF (0x0a), the parser will consider the leading newline as an empty -/// line; the caller should therefore strip it. -class ARROW_EXPORT BlockParser { - public: - explicit BlockParser(ParseOptions options, int32_t num_cols = -1, - int64_t first_row = -1, int32_t max_num_rows = kMaxParserNumRows); - explicit BlockParser(MemoryPool* pool, ParseOptions options, int32_t num_cols = -1, - int64_t first_row = -1, int32_t max_num_rows = kMaxParserNumRows); - ~BlockParser(); - - /// \brief Parse a block of data - /// - /// Parse a block of CSV data, ingesting up to max_num_rows rows. - /// The number of bytes actually parsed is returned in out_size. - Status Parse(util::string_view data, uint32_t* out_size); - - /// \brief Parse sequential blocks of data - /// - /// Only the last block is allowed to be truncated. - Status Parse(const std::vector<util::string_view>& data, uint32_t* out_size); - - /// \brief Parse the final block of data - /// - /// Like Parse(), but called with the final block in a file. - /// The last row may lack a trailing line separator. - Status ParseFinal(util::string_view data, uint32_t* out_size); - - /// \brief Parse the final sequential blocks of data - /// - /// Only the last block is allowed to be truncated. - Status ParseFinal(const std::vector<util::string_view>& data, uint32_t* out_size); - - /// \brief Return the number of parsed rows - int32_t num_rows() const { return parsed_batch().num_rows(); } - /// \brief Return the number of parsed columns - int32_t num_cols() const { return parsed_batch().num_cols(); } - /// \brief Return the total size in bytes of parsed data - uint32_t num_bytes() const { return parsed_batch().num_bytes(); } - /// \brief Return the row number of the first row in the block or -1 if unsupported - int64_t first_row_num() const; - - /// \brief Visit parsed values in a column - /// - /// The signature of the visitor is - /// Status(const uint8_t* data, uint32_t size, bool quoted) - template <typename Visitor> - Status VisitColumn(int32_t col_index, Visitor&& visit) const { - return parsed_batch().VisitColumn(col_index, first_row_num(), - std::forward<Visitor>(visit)); - } - - template <typename Visitor> - Status VisitLastRow(Visitor&& visit) const { - return parsed_batch().VisitLastRow(std::forward<Visitor>(visit)); - } - - protected: - std::unique_ptr<BlockParserImpl> impl_; - - const detail::DataBatch& parsed_batch() const; -}; - -} // namespace csv -} // namespace arrow +// 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 <cstddef> +#include <cstdint> +#include <memory> +#include <vector> + +#include "arrow/buffer.h" +#include "arrow/csv/options.h" +#include "arrow/status.h" +#include "arrow/util/macros.h" +#include "arrow/util/string_view.h" +#include "arrow/util/visibility.h" + +namespace arrow { + +class MemoryPool; + +namespace csv { + +/// Skip at most num_rows from the given input. The input pointer is updated +/// and the number of actually skipped rows is returns (may be less than +/// requested if the input is too short). +ARROW_EXPORT +int32_t SkipRows(const uint8_t* data, uint32_t size, int32_t num_rows, + const uint8_t** out_data); + +class BlockParserImpl; + +namespace detail { + +struct ParsedValueDesc { + uint32_t offset : 31; + bool quoted : 1; +}; + +class ARROW_EXPORT DataBatch { + public: + explicit DataBatch(int32_t num_cols) : num_cols_(num_cols) {} + + /// \brief Return the number of parsed rows + int32_t num_rows() const { return num_rows_; } + /// \brief Return the number of parsed columns + int32_t num_cols() const { return num_cols_; } + /// \brief Return the total size in bytes of parsed data + uint32_t num_bytes() const { return parsed_size_; } + + template <typename Visitor> + Status VisitColumn(int32_t col_index, int64_t first_row, Visitor&& visit) const { + using detail::ParsedValueDesc; + + int64_t row = first_row; + for (size_t buf_index = 0; buf_index < values_buffers_.size(); ++buf_index) { + const auto& values_buffer = values_buffers_[buf_index]; + const auto values = reinterpret_cast<const ParsedValueDesc*>(values_buffer->data()); + const auto max_pos = + static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc)) - 1; + for (int32_t pos = col_index; pos < max_pos; pos += num_cols_, ++row) { + auto start = values[pos].offset; + auto stop = values[pos + 1].offset; + auto quoted = values[pos + 1].quoted; + Status status = visit(parsed_ + start, stop - start, quoted); + if (ARROW_PREDICT_FALSE(!status.ok())) { + if (first_row >= 0) { + status = status.WithMessage("Row #", row, ": ", status.message()); + } + ARROW_RETURN_NOT_OK(status); + } + } + } + return Status::OK(); + } + + template <typename Visitor> + Status VisitLastRow(Visitor&& visit) const { + using detail::ParsedValueDesc; + + const auto& values_buffer = values_buffers_.back(); + const auto values = reinterpret_cast<const ParsedValueDesc*>(values_buffer->data()); + const auto start_pos = + static_cast<int32_t>(values_buffer->size() / sizeof(ParsedValueDesc)) - + num_cols_ - 1; + for (int32_t col_index = 0; col_index < num_cols_; ++col_index) { + auto start = values[start_pos + col_index].offset; + auto stop = values[start_pos + col_index + 1].offset; + auto quoted = values[start_pos + col_index + 1].quoted; + ARROW_RETURN_NOT_OK(visit(parsed_ + start, stop - start, quoted)); + } + return Status::OK(); + } + + protected: + // The number of rows in this batch + int32_t num_rows_ = 0; + // The number of columns + int32_t num_cols_ = 0; + + // XXX should we ensure the parsed buffer is padded with 8 or 16 excess zero bytes? + // It may help with null parsing... + std::vector<std::shared_ptr<Buffer>> values_buffers_; + std::shared_ptr<Buffer> parsed_buffer_; + const uint8_t* parsed_ = NULLPTR; + int32_t parsed_size_ = 0; + + friend class ::arrow::csv::BlockParserImpl; +}; + +} // namespace detail + +constexpr int32_t kMaxParserNumRows = 100000; + +/// \class BlockParser +/// \brief A reusable block-based parser for CSV data +/// +/// The parser takes a block of CSV data and delimits rows and fields, +/// unquoting and unescaping them on the fly. Parsed data is own by the +/// parser, so the original buffer can be discarded after Parse() returns. +/// +/// If the block is truncated (i.e. not all data can be parsed), it is up +/// to the caller to arrange the next block to start with the trailing data. +/// Also, if the previous block ends with CR (0x0d) and a new block starts +/// with LF (0x0a), the parser will consider the leading newline as an empty +/// line; the caller should therefore strip it. +class ARROW_EXPORT BlockParser { + public: + explicit BlockParser(ParseOptions options, int32_t num_cols = -1, + int64_t first_row = -1, int32_t max_num_rows = kMaxParserNumRows); + explicit BlockParser(MemoryPool* pool, ParseOptions options, int32_t num_cols = -1, + int64_t first_row = -1, int32_t max_num_rows = kMaxParserNumRows); + ~BlockParser(); + + /// \brief Parse a block of data + /// + /// Parse a block of CSV data, ingesting up to max_num_rows rows. + /// The number of bytes actually parsed is returned in out_size. + Status Parse(util::string_view data, uint32_t* out_size); + + /// \brief Parse sequential blocks of data + /// + /// Only the last block is allowed to be truncated. + Status Parse(const std::vector<util::string_view>& data, uint32_t* out_size); + + /// \brief Parse the final block of data + /// + /// Like Parse(), but called with the final block in a file. + /// The last row may lack a trailing line separator. + Status ParseFinal(util::string_view data, uint32_t* out_size); + + /// \brief Parse the final sequential blocks of data + /// + /// Only the last block is allowed to be truncated. + Status ParseFinal(const std::vector<util::string_view>& data, uint32_t* out_size); + + /// \brief Return the number of parsed rows + int32_t num_rows() const { return parsed_batch().num_rows(); } + /// \brief Return the number of parsed columns + int32_t num_cols() const { return parsed_batch().num_cols(); } + /// \brief Return the total size in bytes of parsed data + uint32_t num_bytes() const { return parsed_batch().num_bytes(); } + /// \brief Return the row number of the first row in the block or -1 if unsupported + int64_t first_row_num() const; + + /// \brief Visit parsed values in a column + /// + /// The signature of the visitor is + /// Status(const uint8_t* data, uint32_t size, bool quoted) + template <typename Visitor> + Status VisitColumn(int32_t col_index, Visitor&& visit) const { + return parsed_batch().VisitColumn(col_index, first_row_num(), + std::forward<Visitor>(visit)); + } + + template <typename Visitor> + Status VisitLastRow(Visitor&& visit) const { + return parsed_batch().VisitLastRow(std::forward<Visitor>(visit)); + } + + protected: + std::unique_ptr<BlockParserImpl> impl_; + + const detail::DataBatch& parsed_batch() const; +}; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.cc index d31d39ccf8..1a7836561d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.cc @@ -1,1279 +1,1279 @@ -// 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. - -#include "arrow/csv/reader.h" - -#include <cstdint> -#include <cstring> -#include <functional> -#include <limits> -#include <memory> -#include <sstream> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include "arrow/array.h" -#include "arrow/buffer.h" -#include "arrow/csv/chunker.h" -#include "arrow/csv/column_builder.h" -#include "arrow/csv/column_decoder.h" -#include "arrow/csv/options.h" -#include "arrow/csv/parser.h" -#include "arrow/io/interfaces.h" -#include "arrow/result.h" -#include "arrow/status.h" -#include "arrow/table.h" -#include "arrow/type.h" -#include "arrow/type_fwd.h" -#include "arrow/util/async_generator.h" -#include "arrow/util/future.h" -#include "arrow/util/iterator.h" -#include "arrow/util/logging.h" -#include "arrow/util/macros.h" -#include "arrow/util/optional.h" -#include "arrow/util/task_group.h" -#include "arrow/util/thread_pool.h" -#include "arrow/util/utf8.h" -#include "arrow/util/vector.h" - -namespace arrow { -namespace csv { - -using internal::Executor; - -namespace { - -struct ConversionSchema { - struct Column { - std::string name; - // Physical column index in CSV file - int32_t index; - // If true, make a column of nulls - bool is_missing; - // If set, convert the CSV column to this type - // If unset (and is_missing is false), infer the type from the CSV column - std::shared_ptr<DataType> type; - }; - - static Column NullColumn(std::string col_name, std::shared_ptr<DataType> type) { - return Column{std::move(col_name), -1, true, std::move(type)}; - } - - static Column TypedColumn(std::string col_name, int32_t col_index, - std::shared_ptr<DataType> type) { - return Column{std::move(col_name), col_index, false, std::move(type)}; - } - - static Column InferredColumn(std::string col_name, int32_t col_index) { - return Column{std::move(col_name), col_index, false, nullptr}; - } - - std::vector<Column> columns; -}; - -// An iterator of Buffers that makes sure there is no straddling CRLF sequence. -class CSVBufferIterator { - public: - static Iterator<std::shared_ptr<Buffer>> Make( - Iterator<std::shared_ptr<Buffer>> buffer_iterator) { - Transformer<std::shared_ptr<Buffer>, std::shared_ptr<Buffer>> fn = - CSVBufferIterator(); - return MakeTransformedIterator(std::move(buffer_iterator), fn); - } - - static AsyncGenerator<std::shared_ptr<Buffer>> MakeAsync( - AsyncGenerator<std::shared_ptr<Buffer>> buffer_iterator) { - Transformer<std::shared_ptr<Buffer>, std::shared_ptr<Buffer>> fn = - CSVBufferIterator(); - return MakeTransformedGenerator(std::move(buffer_iterator), fn); - } - - Result<TransformFlow<std::shared_ptr<Buffer>>> operator()(std::shared_ptr<Buffer> buf) { - if (buf == nullptr) { - // EOF - return TransformFinish(); - } - - int64_t offset = 0; - if (first_buffer_) { - ARROW_ASSIGN_OR_RAISE(auto data, util::SkipUTF8BOM(buf->data(), buf->size())); - offset += data - buf->data(); - DCHECK_GE(offset, 0); - first_buffer_ = false; - } - - if (trailing_cr_ && buf->data()[offset] == '\n') { - // Skip '\r\n' line separator that started at the end of previous buffer - ++offset; - } - - trailing_cr_ = (buf->data()[buf->size() - 1] == '\r'); - buf = SliceBuffer(buf, offset); - if (buf->size() == 0) { - // EOF - return TransformFinish(); - } else { - return TransformYield(buf); - } - } - - protected: - bool first_buffer_ = true; - // Whether there was a trailing CR at the end of last received buffer - bool trailing_cr_ = false; -}; - -struct CSVBlock { - // (partial + completion + buffer) is an entire delimited CSV buffer. - std::shared_ptr<Buffer> partial; - std::shared_ptr<Buffer> completion; - std::shared_ptr<Buffer> buffer; - int64_t block_index; - bool is_final; - int64_t bytes_skipped; - std::function<Status(int64_t)> consume_bytes; -}; - -} // namespace -} // namespace csv - -template <> -struct IterationTraits<csv::CSVBlock> { - static csv::CSVBlock End() { return csv::CSVBlock{{}, {}, {}, -1, true, 0, {}}; } - static bool IsEnd(const csv::CSVBlock& val) { return val.block_index < 0; } -}; - -namespace csv { -namespace { - -// This is a callable that can be used to transform an iterator. The source iterator -// will contain buffers of data and the output iterator will contain delimited CSV -// blocks. util::optional is used so that there is an end token (required by the -// iterator APIs (e.g. Visit)) even though an empty optional is never used in this code. -class BlockReader { - public: - BlockReader(std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, - int64_t skip_rows) - : chunker_(std::move(chunker)), - partial_(std::make_shared<Buffer>("")), - buffer_(std::move(first_buffer)), - skip_rows_(skip_rows) {} - - protected: - std::unique_ptr<Chunker> chunker_; - std::shared_ptr<Buffer> partial_, buffer_; - int64_t skip_rows_; - int64_t block_index_ = 0; - // Whether there was a trailing CR at the end of last received buffer - bool trailing_cr_ = false; -}; - -// An object that reads delimited CSV blocks for serial use. -// The number of bytes consumed should be notified after each read, -// using CSVBlock::consume_bytes. -class SerialBlockReader : public BlockReader { - public: - using BlockReader::BlockReader; - - static Iterator<CSVBlock> MakeIterator( - Iterator<std::shared_ptr<Buffer>> buffer_iterator, std::unique_ptr<Chunker> chunker, - std::shared_ptr<Buffer> first_buffer, int64_t skip_rows) { - auto block_reader = - std::make_shared<SerialBlockReader>(std::move(chunker), first_buffer, skip_rows); - // Wrap shared pointer in callable - Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = - [block_reader](std::shared_ptr<Buffer> buf) { - return (*block_reader)(std::move(buf)); - }; - return MakeTransformedIterator(std::move(buffer_iterator), block_reader_fn); - } - - static AsyncGenerator<CSVBlock> MakeAsyncIterator( - AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, - std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, - int64_t skip_rows) { - auto block_reader = - std::make_shared<SerialBlockReader>(std::move(chunker), first_buffer, skip_rows); - // Wrap shared pointer in callable - Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = - [block_reader](std::shared_ptr<Buffer> next) { - return (*block_reader)(std::move(next)); - }; - return MakeTransformedGenerator(std::move(buffer_generator), block_reader_fn); - } - - Result<TransformFlow<CSVBlock>> operator()(std::shared_ptr<Buffer> next_buffer) { - if (buffer_ == nullptr) { - return TransformFinish(); - } - - bool is_final = (next_buffer == nullptr); - int64_t bytes_skipped = 0; - - if (skip_rows_) { - bytes_skipped += partial_->size(); - auto orig_size = buffer_->size(); - RETURN_NOT_OK( - chunker_->ProcessSkip(partial_, buffer_, is_final, &skip_rows_, &buffer_)); - bytes_skipped += orig_size - buffer_->size(); - auto empty = std::make_shared<Buffer>(nullptr, 0); - if (skip_rows_) { - // Still have rows beyond this buffer to skip return empty block - partial_ = std::move(buffer_); - buffer_ = next_buffer; - return TransformYield<CSVBlock>(CSVBlock{empty, empty, empty, block_index_++, - is_final, bytes_skipped, - [](int64_t) { return Status::OK(); }}); - } - partial_ = std::move(empty); - } - - std::shared_ptr<Buffer> completion; - - if (is_final) { - // End of file reached => compute completion from penultimate block - RETURN_NOT_OK(chunker_->ProcessFinal(partial_, buffer_, &completion, &buffer_)); - } else { - // Get completion of partial from previous block. - RETURN_NOT_OK( - chunker_->ProcessWithPartial(partial_, buffer_, &completion, &buffer_)); - } - int64_t bytes_before_buffer = partial_->size() + completion->size(); - - auto consume_bytes = [this, bytes_before_buffer, - next_buffer](int64_t nbytes) -> Status { - DCHECK_GE(nbytes, 0); - auto offset = nbytes - bytes_before_buffer; - if (offset < 0) { - // Should not happen - return Status::Invalid("CSV parser got out of sync with chunker"); - } - partial_ = SliceBuffer(buffer_, offset); - buffer_ = next_buffer; - return Status::OK(); - }; - - return TransformYield<CSVBlock>(CSVBlock{partial_, completion, buffer_, - block_index_++, is_final, bytes_skipped, - std::move(consume_bytes)}); - } -}; - -// An object that reads delimited CSV blocks for threaded use. -class ThreadedBlockReader : public BlockReader { - public: - using BlockReader::BlockReader; - - static AsyncGenerator<CSVBlock> MakeAsyncIterator( - AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, - std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, - int64_t skip_rows) { - auto block_reader = std::make_shared<ThreadedBlockReader>(std::move(chunker), - first_buffer, skip_rows); - // Wrap shared pointer in callable - Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = - [block_reader](std::shared_ptr<Buffer> next) { return (*block_reader)(next); }; - return MakeTransformedGenerator(std::move(buffer_generator), block_reader_fn); - } - - Result<TransformFlow<CSVBlock>> operator()(std::shared_ptr<Buffer> next_buffer) { - if (buffer_ == nullptr) { - // EOF - return TransformFinish(); - } - - bool is_final = (next_buffer == nullptr); - - auto current_partial = std::move(partial_); - auto current_buffer = std::move(buffer_); - int64_t bytes_skipped = 0; - - if (skip_rows_) { - auto orig_size = current_buffer->size(); - bytes_skipped = current_partial->size(); - RETURN_NOT_OK(chunker_->ProcessSkip(current_partial, current_buffer, is_final, - &skip_rows_, ¤t_buffer)); - bytes_skipped += orig_size - current_buffer->size(); - current_partial = std::make_shared<Buffer>(nullptr, 0); - if (skip_rows_) { - partial_ = std::move(current_buffer); - buffer_ = std::move(next_buffer); - return TransformYield<CSVBlock>(CSVBlock{current_partial, - current_partial, - current_partial, - block_index_++, - is_final, - bytes_skipped, - {}}); - } - } - - std::shared_ptr<Buffer> whole, completion, next_partial; - - if (is_final) { - // End of file reached => compute completion from penultimate block - RETURN_NOT_OK( - chunker_->ProcessFinal(current_partial, current_buffer, &completion, &whole)); - } else { - // Get completion of partial from previous block. - std::shared_ptr<Buffer> starts_with_whole; - // Get completion of partial from previous block. - RETURN_NOT_OK(chunker_->ProcessWithPartial(current_partial, current_buffer, - &completion, &starts_with_whole)); - - // Get a complete CSV block inside `partial + block`, and keep - // the rest for the next iteration. - RETURN_NOT_OK(chunker_->Process(starts_with_whole, &whole, &next_partial)); - } - - partial_ = std::move(next_partial); - buffer_ = std::move(next_buffer); - - return TransformYield<CSVBlock>(CSVBlock{ - current_partial, completion, whole, block_index_++, is_final, bytes_skipped, {}}); - } -}; - -struct ParsedBlock { - std::shared_ptr<BlockParser> parser; - int64_t block_index; - int64_t bytes_parsed_or_skipped; -}; - -struct DecodedBlock { - std::shared_ptr<RecordBatch> record_batch; - // Represents the number of input bytes represented by this batch - // This will include bytes skipped when skipping rows after the header - int64_t bytes_processed; -}; - -} // namespace - -} // namespace csv - -template <> -struct IterationTraits<csv::ParsedBlock> { - static csv::ParsedBlock End() { return csv::ParsedBlock{nullptr, -1, -1}; } - static bool IsEnd(const csv::ParsedBlock& val) { return val.block_index < 0; } -}; - -template <> -struct IterationTraits<csv::DecodedBlock> { - static csv::DecodedBlock End() { return csv::DecodedBlock{nullptr, -1}; } - static bool IsEnd(const csv::DecodedBlock& val) { return val.bytes_processed < 0; } -}; - -namespace csv { -namespace { - -// A function object that takes in a buffer of CSV data and returns a parsed batch of CSV -// data (CSVBlock -> ParsedBlock) for use with MakeMappedGenerator. -// The parsed batch contains a list of offsets for each of the columns so that columns -// can be individually scanned -// -// This operator is not re-entrant -class BlockParsingOperator { - public: - BlockParsingOperator(io::IOContext io_context, ParseOptions parse_options, - int num_csv_cols, int64_t first_row) - : io_context_(io_context), - parse_options_(parse_options), - num_csv_cols_(num_csv_cols), - count_rows_(first_row >= 0), - num_rows_seen_(first_row) {} - - Result<ParsedBlock> operator()(const CSVBlock& block) { - constexpr int32_t max_num_rows = std::numeric_limits<int32_t>::max(); - auto parser = std::make_shared<BlockParser>( - io_context_.pool(), parse_options_, num_csv_cols_, num_rows_seen_, max_num_rows); - - std::shared_ptr<Buffer> straddling; - std::vector<util::string_view> views; - if (block.partial->size() != 0 || block.completion->size() != 0) { - if (block.partial->size() == 0) { - straddling = block.completion; - } else if (block.completion->size() == 0) { - straddling = block.partial; - } else { - ARROW_ASSIGN_OR_RAISE( - straddling, - ConcatenateBuffers({block.partial, block.completion}, io_context_.pool())); - } - views = {util::string_view(*straddling), util::string_view(*block.buffer)}; - } else { - views = {util::string_view(*block.buffer)}; - } - uint32_t parsed_size; - if (block.is_final) { - RETURN_NOT_OK(parser->ParseFinal(views, &parsed_size)); - } else { - RETURN_NOT_OK(parser->Parse(views, &parsed_size)); - } - if (count_rows_) { - num_rows_seen_ += parser->num_rows(); - } - RETURN_NOT_OK(block.consume_bytes(parsed_size)); - return ParsedBlock{std::move(parser), block.block_index, - static_cast<int64_t>(parsed_size) + block.bytes_skipped}; - } - - private: - io::IOContext io_context_; - ParseOptions parse_options_; - int num_csv_cols_; - bool count_rows_; - int64_t num_rows_seen_; -}; - -// A function object that takes in parsed batch of CSV data and decodes it to an arrow -// record batch (ParsedBlock -> DecodedBlock) for use with MakeMappedGenerator. -class BlockDecodingOperator { - public: - Future<DecodedBlock> operator()(const ParsedBlock& block) { - DCHECK(!state_->column_decoders.empty()); - std::vector<Future<std::shared_ptr<Array>>> decoded_array_futs; - for (auto& decoder : state_->column_decoders) { - decoded_array_futs.push_back(decoder->Decode(block.parser)); - } - auto bytes_parsed_or_skipped = block.bytes_parsed_or_skipped; - auto decoded_arrays_fut = All(std::move(decoded_array_futs)); - auto state = state_; - return decoded_arrays_fut.Then( - [state, bytes_parsed_or_skipped]( - const std::vector<Result<std::shared_ptr<Array>>>& maybe_decoded_arrays) - -> Result<DecodedBlock> { - ARROW_ASSIGN_OR_RAISE(auto decoded_arrays, - internal::UnwrapOrRaise(maybe_decoded_arrays)); - - ARROW_ASSIGN_OR_RAISE(auto batch, - state->DecodedArraysToBatch(std::move(decoded_arrays))); - return DecodedBlock{std::move(batch), bytes_parsed_or_skipped}; - }); - } - - static Result<BlockDecodingOperator> Make(io::IOContext io_context, - ConvertOptions convert_options, - ConversionSchema conversion_schema) { - BlockDecodingOperator op(std::move(io_context), std::move(convert_options), - std::move(conversion_schema)); - RETURN_NOT_OK(op.state_->MakeColumnDecoders(io_context)); - return op; - } - - private: - BlockDecodingOperator(io::IOContext io_context, ConvertOptions convert_options, - ConversionSchema conversion_schema) - : state_(std::make_shared<State>(std::move(io_context), std::move(convert_options), - std::move(conversion_schema))) {} - - struct State { - State(io::IOContext io_context, ConvertOptions convert_options, - ConversionSchema conversion_schema) - : convert_options(std::move(convert_options)), - conversion_schema(std::move(conversion_schema)) {} - - Result<std::shared_ptr<RecordBatch>> DecodedArraysToBatch( - std::vector<std::shared_ptr<Array>> arrays) { - if (schema == nullptr) { - FieldVector fields(arrays.size()); - for (size_t i = 0; i < arrays.size(); ++i) { - fields[i] = field(conversion_schema.columns[i].name, arrays[i]->type()); - } - schema = arrow::schema(std::move(fields)); - } - const auto n_rows = arrays[0]->length(); - return RecordBatch::Make(schema, n_rows, std::move(arrays)); - } - - // Make column decoders from conversion schema - Status MakeColumnDecoders(io::IOContext io_context) { - for (const auto& column : conversion_schema.columns) { - std::shared_ptr<ColumnDecoder> decoder; - if (column.is_missing) { - ARROW_ASSIGN_OR_RAISE(decoder, - ColumnDecoder::MakeNull(io_context.pool(), column.type)); - } else if (column.type != nullptr) { - ARROW_ASSIGN_OR_RAISE( - decoder, ColumnDecoder::Make(io_context.pool(), column.type, column.index, - convert_options)); - } else { - ARROW_ASSIGN_OR_RAISE( - decoder, - ColumnDecoder::Make(io_context.pool(), column.index, convert_options)); - } - column_decoders.push_back(std::move(decoder)); - } - return Status::OK(); - } - - ConvertOptions convert_options; - ConversionSchema conversion_schema; - std::vector<std::shared_ptr<ColumnDecoder>> column_decoders; - std::shared_ptr<Schema> schema; - }; - - std::shared_ptr<State> state_; -}; - -///////////////////////////////////////////////////////////////////////// -// Base class for common functionality - -class ReaderMixin { - public: - ReaderMixin(io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options, bool count_rows) - : io_context_(std::move(io_context)), - read_options_(read_options), - parse_options_(parse_options), - convert_options_(convert_options), - count_rows_(count_rows), - num_rows_seen_(count_rows_ ? 1 : -1), - input_(std::move(input)) {} - - protected: - // Read header and column names from buffer, create column builders - // Returns the # of bytes consumed - Result<int64_t> ProcessHeader(const std::shared_ptr<Buffer>& buf, - std::shared_ptr<Buffer>* rest) { - const uint8_t* data = buf->data(); - const auto data_end = data + buf->size(); - DCHECK_GT(data_end - data, 0); - - if (read_options_.skip_rows) { - // Skip initial rows (potentially invalid CSV data) - auto num_skipped_rows = SkipRows(data, static_cast<uint32_t>(data_end - data), - read_options_.skip_rows, &data); - if (num_skipped_rows < read_options_.skip_rows) { - return Status::Invalid( - "Could not skip initial ", read_options_.skip_rows, - " rows from CSV file, " - "either file is too short or header is larger than block size"); - } - if (count_rows_) { - num_rows_seen_ += num_skipped_rows; - } - } - - if (read_options_.column_names.empty()) { - // Parse one row (either to read column names or to know the number of columns) - BlockParser parser(io_context_.pool(), parse_options_, num_csv_cols_, - num_rows_seen_, 1); - uint32_t parsed_size = 0; - RETURN_NOT_OK(parser.Parse( - util::string_view(reinterpret_cast<const char*>(data), data_end - data), - &parsed_size)); - if (parser.num_rows() != 1) { - return Status::Invalid( - "Could not read first row from CSV file, either " - "file is too short or header is larger than block size"); - } - if (parser.num_cols() == 0) { - return Status::Invalid("No columns in CSV file"); - } - - if (read_options_.autogenerate_column_names) { - column_names_ = GenerateColumnNames(parser.num_cols()); - } else { - // Read column names from header row - auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { - column_names_.emplace_back(reinterpret_cast<const char*>(data), size); - return Status::OK(); - }; - RETURN_NOT_OK(parser.VisitLastRow(visit)); - DCHECK_EQ(static_cast<size_t>(parser.num_cols()), column_names_.size()); - // Skip parsed header row - data += parsed_size; - if (count_rows_) { - ++num_rows_seen_; - } - } - } else { - column_names_ = read_options_.column_names; - } - - if (count_rows_) { - // increase rows seen to skip past rows which will be skipped - num_rows_seen_ += read_options_.skip_rows_after_names; - } - - auto bytes_consumed = data - buf->data(); - *rest = SliceBuffer(buf, bytes_consumed); - - num_csv_cols_ = static_cast<int32_t>(column_names_.size()); - DCHECK_GT(num_csv_cols_, 0); - - RETURN_NOT_OK(MakeConversionSchema()); - return bytes_consumed; - } - - std::vector<std::string> GenerateColumnNames(int32_t num_cols) { - std::vector<std::string> res; - res.reserve(num_cols); - for (int32_t i = 0; i < num_cols; ++i) { - std::stringstream ss; - ss << "f" << i; - res.push_back(ss.str()); - } - return res; - } - - // Make conversion schema from options and parsed CSV header - Status MakeConversionSchema() { - // Append a column converted from CSV data - auto append_csv_column = [&](std::string col_name, int32_t col_index) { - // Does the named column have a fixed type? - auto it = convert_options_.column_types.find(col_name); - if (it == convert_options_.column_types.end()) { - conversion_schema_.columns.push_back( - ConversionSchema::InferredColumn(std::move(col_name), col_index)); - } else { - conversion_schema_.columns.push_back( - ConversionSchema::TypedColumn(std::move(col_name), col_index, it->second)); - } - }; - - // Append a column of nulls - auto append_null_column = [&](std::string col_name) { - // If the named column has a fixed type, use it, otherwise use null() - std::shared_ptr<DataType> type; - auto it = convert_options_.column_types.find(col_name); - if (it == convert_options_.column_types.end()) { - type = null(); - } else { - type = it->second; - } - conversion_schema_.columns.push_back( - ConversionSchema::NullColumn(std::move(col_name), std::move(type))); - }; - - if (convert_options_.include_columns.empty()) { - // Include all columns in CSV file order - for (int32_t col_index = 0; col_index < num_csv_cols_; ++col_index) { - append_csv_column(column_names_[col_index], col_index); - } - } else { - // Include columns from `include_columns` (in that order) - // Compute indices of columns in the CSV file - std::unordered_map<std::string, int32_t> col_indices; - col_indices.reserve(column_names_.size()); - for (int32_t i = 0; i < static_cast<int32_t>(column_names_.size()); ++i) { - col_indices.emplace(column_names_[i], i); - } - - for (const auto& col_name : convert_options_.include_columns) { - auto it = col_indices.find(col_name); - if (it != col_indices.end()) { - append_csv_column(col_name, it->second); - } else if (convert_options_.include_missing_columns) { - append_null_column(col_name); - } else { - return Status::KeyError("Column '", col_name, - "' in include_columns " - "does not exist in CSV file"); - } - } - } - return Status::OK(); - } - - struct ParseResult { - std::shared_ptr<BlockParser> parser; - int64_t parsed_bytes; - }; - - Result<ParseResult> Parse(const std::shared_ptr<Buffer>& partial, - const std::shared_ptr<Buffer>& completion, - const std::shared_ptr<Buffer>& block, int64_t block_index, - bool is_final) { - static constexpr int32_t max_num_rows = std::numeric_limits<int32_t>::max(); - auto parser = std::make_shared<BlockParser>( - io_context_.pool(), parse_options_, num_csv_cols_, num_rows_seen_, max_num_rows); - - std::shared_ptr<Buffer> straddling; - std::vector<util::string_view> views; - if (partial->size() != 0 || completion->size() != 0) { - if (partial->size() == 0) { - straddling = completion; - } else if (completion->size() == 0) { - straddling = partial; - } else { - ARROW_ASSIGN_OR_RAISE( - straddling, ConcatenateBuffers({partial, completion}, io_context_.pool())); - } - views = {util::string_view(*straddling), util::string_view(*block)}; - } else { - views = {util::string_view(*block)}; - } - uint32_t parsed_size; - if (is_final) { - RETURN_NOT_OK(parser->ParseFinal(views, &parsed_size)); - } else { - RETURN_NOT_OK(parser->Parse(views, &parsed_size)); - } - if (count_rows_) { - num_rows_seen_ += parser->num_rows(); - } - return ParseResult{std::move(parser), static_cast<int64_t>(parsed_size)}; - } - - io::IOContext io_context_; - ReadOptions read_options_; - ParseOptions parse_options_; - ConvertOptions convert_options_; - - // Number of columns in the CSV file - int32_t num_csv_cols_ = -1; - // Whether num_rows_seen_ tracks the number of rows seen in the CSV being parsed - bool count_rows_; - // Number of rows seen in the csv. Not used if count_rows is false - int64_t num_rows_seen_; - // Column names in the CSV file - std::vector<std::string> column_names_; - ConversionSchema conversion_schema_; - - std::shared_ptr<io::InputStream> input_; - std::shared_ptr<internal::TaskGroup> task_group_; -}; - -///////////////////////////////////////////////////////////////////////// -// Base class for one-shot table readers - -class BaseTableReader : public ReaderMixin, public csv::TableReader { - public: - using ReaderMixin::ReaderMixin; - - virtual Status Init() = 0; - - Future<std::shared_ptr<Table>> ReadAsync() override { - return Future<std::shared_ptr<Table>>::MakeFinished(Read()); - } - - protected: - // Make column builders from conversion schema - Status MakeColumnBuilders() { - for (const auto& column : conversion_schema_.columns) { - std::shared_ptr<ColumnBuilder> builder; - if (column.is_missing) { - ARROW_ASSIGN_OR_RAISE(builder, ColumnBuilder::MakeNull(io_context_.pool(), - column.type, task_group_)); - } else if (column.type != nullptr) { - ARROW_ASSIGN_OR_RAISE( - builder, ColumnBuilder::Make(io_context_.pool(), column.type, column.index, - convert_options_, task_group_)); - } else { - ARROW_ASSIGN_OR_RAISE(builder, - ColumnBuilder::Make(io_context_.pool(), column.index, - convert_options_, task_group_)); - } - column_builders_.push_back(std::move(builder)); - } - return Status::OK(); - } - - Result<int64_t> ParseAndInsert(const std::shared_ptr<Buffer>& partial, - const std::shared_ptr<Buffer>& completion, - const std::shared_ptr<Buffer>& block, - int64_t block_index, bool is_final) { - ARROW_ASSIGN_OR_RAISE(auto result, - Parse(partial, completion, block, block_index, is_final)); - RETURN_NOT_OK(ProcessData(result.parser, block_index)); - return result.parsed_bytes; - } - - // Trigger conversion of parsed block data - Status ProcessData(const std::shared_ptr<BlockParser>& parser, int64_t block_index) { - for (auto& builder : column_builders_) { - builder->Insert(block_index, parser); - } - return Status::OK(); - } - - Result<std::shared_ptr<Table>> MakeTable() { - DCHECK_EQ(column_builders_.size(), conversion_schema_.columns.size()); - - std::vector<std::shared_ptr<Field>> fields; - std::vector<std::shared_ptr<ChunkedArray>> columns; - - for (int32_t i = 0; i < static_cast<int32_t>(column_builders_.size()); ++i) { - const auto& column = conversion_schema_.columns[i]; - ARROW_ASSIGN_OR_RAISE(auto array, column_builders_[i]->Finish()); - fields.push_back(::arrow::field(column.name, array->type())); - columns.emplace_back(std::move(array)); - } - return Table::Make(schema(std::move(fields)), std::move(columns)); - } - - // Column builders for target Table (in ConversionSchema order) - std::vector<std::shared_ptr<ColumnBuilder>> column_builders_; -}; - -///////////////////////////////////////////////////////////////////////// -// Base class for streaming readers - -class StreamingReaderImpl : public ReaderMixin, - public csv::StreamingReader, - public std::enable_shared_from_this<StreamingReaderImpl> { - public: - StreamingReaderImpl(io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options, bool count_rows) - : ReaderMixin(io_context, std::move(input), read_options, parse_options, - convert_options, count_rows), - bytes_decoded_(std::make_shared<std::atomic<int64_t>>(0)) {} - - Future<> Init(Executor* cpu_executor) { - ARROW_ASSIGN_OR_RAISE(auto istream_it, - io::MakeInputStreamIterator(input_, read_options_.block_size)); - - // TODO Consider exposing readahead as a read option (ARROW-12090) - ARROW_ASSIGN_OR_RAISE(auto bg_it, MakeBackgroundGenerator(std::move(istream_it), - io_context_.executor())); - - auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor); - - auto buffer_generator = CSVBufferIterator::MakeAsync(std::move(transferred_it)); - - int max_readahead = cpu_executor->GetCapacity(); - auto self = shared_from_this(); - - return buffer_generator().Then([self, buffer_generator, max_readahead]( - const std::shared_ptr<Buffer>& first_buffer) { - return self->InitAfterFirstBuffer(first_buffer, buffer_generator, max_readahead); - }); - } - - std::shared_ptr<Schema> schema() const override { return schema_; } - - int64_t bytes_read() const override { return bytes_decoded_->load(); } - - Status ReadNext(std::shared_ptr<RecordBatch>* batch) override { - auto next_fut = ReadNextAsync(); - auto next_result = next_fut.result(); - return std::move(next_result).Value(batch); - } - - Future<std::shared_ptr<RecordBatch>> ReadNextAsync() override { - return record_batch_gen_(); - } - - protected: - Future<> InitAfterFirstBuffer(const std::shared_ptr<Buffer>& first_buffer, - AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, - int max_readahead) { - if (first_buffer == nullptr) { - return Status::Invalid("Empty CSV file"); - } - - std::shared_ptr<Buffer> after_header; - ARROW_ASSIGN_OR_RAISE(auto header_bytes_consumed, - ProcessHeader(first_buffer, &after_header)); - bytes_decoded_->fetch_add(header_bytes_consumed); - - auto parser_op = - BlockParsingOperator(io_context_, parse_options_, num_csv_cols_, num_rows_seen_); - ARROW_ASSIGN_OR_RAISE( - auto decoder_op, - BlockDecodingOperator::Make(io_context_, convert_options_, conversion_schema_)); - - auto block_gen = SerialBlockReader::MakeAsyncIterator( - std::move(buffer_generator), MakeChunker(parse_options_), std::move(after_header), - read_options_.skip_rows_after_names); - auto parsed_block_gen = - MakeMappedGenerator(std::move(block_gen), std::move(parser_op)); - auto rb_gen = MakeMappedGenerator(std::move(parsed_block_gen), std::move(decoder_op)); - - auto self = shared_from_this(); - return rb_gen().Then([self, rb_gen, max_readahead](const DecodedBlock& first_block) { - return self->InitAfterFirstBatch(first_block, std::move(rb_gen), max_readahead); - }); - } - - Status InitAfterFirstBatch(const DecodedBlock& first_block, - AsyncGenerator<DecodedBlock> batch_gen, int max_readahead) { - schema_ = first_block.record_batch->schema(); - - AsyncGenerator<DecodedBlock> readahead_gen; - if (read_options_.use_threads) { - readahead_gen = MakeReadaheadGenerator(std::move(batch_gen), max_readahead); - } else { - readahead_gen = std::move(batch_gen); - } - - AsyncGenerator<DecodedBlock> restarted_gen; - // Streaming reader should not emit empty record batches - if (first_block.record_batch->num_rows() > 0) { - restarted_gen = MakeGeneratorStartsWith({first_block}, std::move(readahead_gen)); - } else { - restarted_gen = std::move(readahead_gen); - } - - auto bytes_decoded = bytes_decoded_; - auto unwrap_and_record_bytes = - [bytes_decoded]( - const DecodedBlock& block) -> Result<std::shared_ptr<RecordBatch>> { - bytes_decoded->fetch_add(block.bytes_processed); - return block.record_batch; - }; - - auto unwrapped = - MakeMappedGenerator(std::move(restarted_gen), std::move(unwrap_and_record_bytes)); - - record_batch_gen_ = MakeCancellable(std::move(unwrapped), io_context_.stop_token()); - return Status::OK(); - } - - std::shared_ptr<Schema> schema_; - AsyncGenerator<std::shared_ptr<RecordBatch>> record_batch_gen_; - // bytes which have been decoded and asked for by the caller - std::shared_ptr<std::atomic<int64_t>> bytes_decoded_; -}; - -///////////////////////////////////////////////////////////////////////// -// Serial TableReader implementation - -class SerialTableReader : public BaseTableReader { - public: - using BaseTableReader::BaseTableReader; - - Status Init() override { - ARROW_ASSIGN_OR_RAISE(auto istream_it, - io::MakeInputStreamIterator(input_, read_options_.block_size)); - - // Since we're converting serially, no need to readahead more than one block - int32_t block_queue_size = 1; - ARROW_ASSIGN_OR_RAISE(auto rh_it, - MakeReadaheadIterator(std::move(istream_it), block_queue_size)); - buffer_iterator_ = CSVBufferIterator::Make(std::move(rh_it)); - return Status::OK(); - } - - Result<std::shared_ptr<Table>> Read() override { - task_group_ = internal::TaskGroup::MakeSerial(io_context_.stop_token()); - - // First block - ARROW_ASSIGN_OR_RAISE(auto first_buffer, buffer_iterator_.Next()); - if (first_buffer == nullptr) { - return Status::Invalid("Empty CSV file"); - } - RETURN_NOT_OK(ProcessHeader(first_buffer, &first_buffer)); - RETURN_NOT_OK(MakeColumnBuilders()); - - auto block_iterator = SerialBlockReader::MakeIterator( - std::move(buffer_iterator_), MakeChunker(parse_options_), std::move(first_buffer), - read_options_.skip_rows_after_names); - while (true) { - RETURN_NOT_OK(io_context_.stop_token().Poll()); - - ARROW_ASSIGN_OR_RAISE(auto maybe_block, block_iterator.Next()); - if (IsIterationEnd(maybe_block)) { - // EOF - break; - } - ARROW_ASSIGN_OR_RAISE( - int64_t parsed_bytes, - ParseAndInsert(maybe_block.partial, maybe_block.completion, maybe_block.buffer, - maybe_block.block_index, maybe_block.is_final)); - RETURN_NOT_OK(maybe_block.consume_bytes(parsed_bytes)); - } - // Finish conversion, create schema and table - RETURN_NOT_OK(task_group_->Finish()); - return MakeTable(); - } - - protected: - Iterator<std::shared_ptr<Buffer>> buffer_iterator_; -}; - -class AsyncThreadedTableReader - : public BaseTableReader, - public std::enable_shared_from_this<AsyncThreadedTableReader> { - public: - using BaseTableReader::BaseTableReader; - - AsyncThreadedTableReader(io::IOContext io_context, - std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, - const ParseOptions& parse_options, - const ConvertOptions& convert_options, Executor* cpu_executor) - // Count rows is currently not supported during parallel read - : BaseTableReader(std::move(io_context), input, read_options, parse_options, - convert_options, /*count_rows=*/false), - cpu_executor_(cpu_executor) {} - - ~AsyncThreadedTableReader() override { - if (task_group_) { - // In case of error, make sure all pending tasks are finished before - // we start destroying BaseTableReader members - ARROW_UNUSED(task_group_->Finish()); - } - } - - Status Init() override { - ARROW_ASSIGN_OR_RAISE(auto istream_it, - io::MakeInputStreamIterator(input_, read_options_.block_size)); - - int max_readahead = cpu_executor_->GetCapacity(); - int readahead_restart = std::max(1, max_readahead / 2); - - ARROW_ASSIGN_OR_RAISE( - auto bg_it, MakeBackgroundGenerator(std::move(istream_it), io_context_.executor(), - max_readahead, readahead_restart)); - - auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor_); - buffer_generator_ = CSVBufferIterator::MakeAsync(std::move(transferred_it)); - return Status::OK(); - } - - Result<std::shared_ptr<Table>> Read() override { return ReadAsync().result(); } - - Future<std::shared_ptr<Table>> ReadAsync() override { - task_group_ = - internal::TaskGroup::MakeThreaded(cpu_executor_, io_context_.stop_token()); - - auto self = shared_from_this(); - return ProcessFirstBuffer().Then([self](const std::shared_ptr<Buffer>& first_buffer) { - auto block_generator = ThreadedBlockReader::MakeAsyncIterator( - self->buffer_generator_, MakeChunker(self->parse_options_), - std::move(first_buffer), self->read_options_.skip_rows_after_names); - - std::function<Status(CSVBlock)> block_visitor = - [self](CSVBlock maybe_block) -> Status { - // The logic in VisitAsyncGenerator ensures that we will never be - // passed an empty block (visit does not call with the end token) so - // we can be assured maybe_block has a value. - DCHECK_GE(maybe_block.block_index, 0); - DCHECK(!maybe_block.consume_bytes); - - // Launch parse task - self->task_group_->Append([self, maybe_block] { - return self - ->ParseAndInsert(maybe_block.partial, maybe_block.completion, - maybe_block.buffer, maybe_block.block_index, - maybe_block.is_final) - .status(); - }); - return Status::OK(); - }; - - return VisitAsyncGenerator(std::move(block_generator), block_visitor) - .Then([self]() -> Future<> { - // By this point we've added all top level tasks so it is safe to call - // FinishAsync - return self->task_group_->FinishAsync(); - }) - .Then([self]() -> Result<std::shared_ptr<Table>> { - // Finish conversion, create schema and table - return self->MakeTable(); - }); - }); - } - - protected: - Future<std::shared_ptr<Buffer>> ProcessFirstBuffer() { - // First block - auto first_buffer_future = buffer_generator_(); - return first_buffer_future.Then([this](const std::shared_ptr<Buffer>& first_buffer) - -> Result<std::shared_ptr<Buffer>> { - if (first_buffer == nullptr) { - return Status::Invalid("Empty CSV file"); - } - std::shared_ptr<Buffer> first_buffer_processed; - RETURN_NOT_OK(ProcessHeader(first_buffer, &first_buffer_processed)); - RETURN_NOT_OK(MakeColumnBuilders()); - return first_buffer_processed; - }); - } - - Executor* cpu_executor_; - AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator_; -}; - -Result<std::shared_ptr<TableReader>> MakeTableReader( - MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options) { - RETURN_NOT_OK(parse_options.Validate()); - RETURN_NOT_OK(read_options.Validate()); - RETURN_NOT_OK(convert_options.Validate()); - std::shared_ptr<BaseTableReader> reader; - if (read_options.use_threads) { - auto cpu_executor = internal::GetCpuThreadPool(); - reader = std::make_shared<AsyncThreadedTableReader>( - io_context, input, read_options, parse_options, convert_options, cpu_executor); - } else { - reader = std::make_shared<SerialTableReader>(io_context, input, read_options, - parse_options, convert_options, - /*count_rows=*/true); - } - RETURN_NOT_OK(reader->Init()); - return reader; -} - -Future<std::shared_ptr<StreamingReader>> MakeStreamingReader( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - internal::Executor* cpu_executor, const ReadOptions& read_options, - const ParseOptions& parse_options, const ConvertOptions& convert_options) { - RETURN_NOT_OK(parse_options.Validate()); - RETURN_NOT_OK(read_options.Validate()); - RETURN_NOT_OK(convert_options.Validate()); - std::shared_ptr<StreamingReaderImpl> reader; - reader = std::make_shared<StreamingReaderImpl>( - io_context, input, read_options, parse_options, convert_options, - /*count_rows=*/!read_options.use_threads || cpu_executor->GetCapacity() == 1); - return reader->Init(cpu_executor).Then([reader] { - return std::dynamic_pointer_cast<StreamingReader>(reader); - }); -} - -///////////////////////////////////////////////////////////////////////// -// Row count implementation - -class CSVRowCounter : public ReaderMixin, - public std::enable_shared_from_this<CSVRowCounter> { - public: - CSVRowCounter(io::IOContext io_context, Executor* cpu_executor, - std::shared_ptr<io::InputStream> input, const ReadOptions& read_options, - const ParseOptions& parse_options) - : ReaderMixin(io_context, std::move(input), read_options, parse_options, - ConvertOptions::Defaults(), /*count_rows=*/true), - cpu_executor_(cpu_executor), - row_count_(0) {} - - Future<int64_t> Count() { - auto self = shared_from_this(); - return Init(self).Then([self]() { return self->DoCount(self); }); - } - - private: - Future<> Init(const std::shared_ptr<CSVRowCounter>& self) { - ARROW_ASSIGN_OR_RAISE(auto istream_it, - io::MakeInputStreamIterator(input_, read_options_.block_size)); - // TODO Consider exposing readahead as a read option (ARROW-12090) - ARROW_ASSIGN_OR_RAISE(auto bg_it, MakeBackgroundGenerator(std::move(istream_it), - io_context_.executor())); - auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor_); - auto buffer_generator = CSVBufferIterator::MakeAsync(std::move(transferred_it)); - - return buffer_generator().Then( - [self, buffer_generator](std::shared_ptr<Buffer> first_buffer) { - if (!first_buffer) { - return Status::Invalid("Empty CSV file"); - } - RETURN_NOT_OK(self->ProcessHeader(first_buffer, &first_buffer)); - self->block_generator_ = SerialBlockReader::MakeAsyncIterator( - buffer_generator, MakeChunker(self->parse_options_), - std::move(first_buffer), 0); - return Status::OK(); - }); - } - - Future<int64_t> DoCount(const std::shared_ptr<CSVRowCounter>& self) { - // count_cb must return a value instead of Status/Future<> to work with - // MakeMappedGenerator, and it must use a type with a valid end value to work with - // IterationEnd. - std::function<Result<util::optional<int64_t>>(const CSVBlock&)> count_cb = - [self](const CSVBlock& maybe_block) -> Result<util::optional<int64_t>> { - ARROW_ASSIGN_OR_RAISE( - auto parser, - self->Parse(maybe_block.partial, maybe_block.completion, maybe_block.buffer, - maybe_block.block_index, maybe_block.is_final)); - RETURN_NOT_OK(maybe_block.consume_bytes(parser.parsed_bytes)); - self->row_count_ += parser.parser->num_rows(); - return parser.parser->num_rows(); - }; - auto count_gen = MakeMappedGenerator(block_generator_, std::move(count_cb)); - return DiscardAllFromAsyncGenerator(count_gen).Then( - [self]() { return self->row_count_; }); - } - - Executor* cpu_executor_; - AsyncGenerator<CSVBlock> block_generator_; - int64_t row_count_; -}; - -} // namespace - -///////////////////////////////////////////////////////////////////////// -// Factory functions - -Result<std::shared_ptr<TableReader>> TableReader::Make( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options) { - return MakeTableReader(io_context.pool(), io_context, std::move(input), read_options, - parse_options, convert_options); -} - -Result<std::shared_ptr<TableReader>> TableReader::Make( - MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options) { - return MakeTableReader(pool, io_context, std::move(input), read_options, parse_options, - convert_options); -} - -Result<std::shared_ptr<StreamingReader>> StreamingReader::Make( - MemoryPool* pool, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options) { - auto io_context = io::IOContext(pool); - auto cpu_executor = internal::GetCpuThreadPool(); - auto reader_fut = MakeStreamingReader(io_context, std::move(input), cpu_executor, - read_options, parse_options, convert_options); - auto reader_result = reader_fut.result(); - ARROW_ASSIGN_OR_RAISE(auto reader, reader_result); - return reader; -} - -Result<std::shared_ptr<StreamingReader>> StreamingReader::Make( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options) { - auto cpu_executor = internal::GetCpuThreadPool(); - auto reader_fut = MakeStreamingReader(io_context, std::move(input), cpu_executor, - read_options, parse_options, convert_options); - auto reader_result = reader_fut.result(); - ARROW_ASSIGN_OR_RAISE(auto reader, reader_result); - return reader; -} - -Future<std::shared_ptr<StreamingReader>> StreamingReader::MakeAsync( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - internal::Executor* cpu_executor, const ReadOptions& read_options, - const ParseOptions& parse_options, const ConvertOptions& convert_options) { - return MakeStreamingReader(io_context, std::move(input), cpu_executor, read_options, - parse_options, convert_options); -} - -Future<int64_t> CountRowsAsync(io::IOContext io_context, - std::shared_ptr<io::InputStream> input, - internal::Executor* cpu_executor, - const ReadOptions& read_options, - const ParseOptions& parse_options) { - RETURN_NOT_OK(parse_options.Validate()); - RETURN_NOT_OK(read_options.Validate()); - auto counter = std::make_shared<CSVRowCounter>( - io_context, cpu_executor, std::move(input), read_options, parse_options); - return counter->Count(); -} - -} // namespace csv - -} // namespace arrow +// 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. + +#include "arrow/csv/reader.h" + +#include <cstdint> +#include <cstring> +#include <functional> +#include <limits> +#include <memory> +#include <sstream> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "arrow/array.h" +#include "arrow/buffer.h" +#include "arrow/csv/chunker.h" +#include "arrow/csv/column_builder.h" +#include "arrow/csv/column_decoder.h" +#include "arrow/csv/options.h" +#include "arrow/csv/parser.h" +#include "arrow/io/interfaces.h" +#include "arrow/result.h" +#include "arrow/status.h" +#include "arrow/table.h" +#include "arrow/type.h" +#include "arrow/type_fwd.h" +#include "arrow/util/async_generator.h" +#include "arrow/util/future.h" +#include "arrow/util/iterator.h" +#include "arrow/util/logging.h" +#include "arrow/util/macros.h" +#include "arrow/util/optional.h" +#include "arrow/util/task_group.h" +#include "arrow/util/thread_pool.h" +#include "arrow/util/utf8.h" +#include "arrow/util/vector.h" + +namespace arrow { +namespace csv { + +using internal::Executor; + +namespace { + +struct ConversionSchema { + struct Column { + std::string name; + // Physical column index in CSV file + int32_t index; + // If true, make a column of nulls + bool is_missing; + // If set, convert the CSV column to this type + // If unset (and is_missing is false), infer the type from the CSV column + std::shared_ptr<DataType> type; + }; + + static Column NullColumn(std::string col_name, std::shared_ptr<DataType> type) { + return Column{std::move(col_name), -1, true, std::move(type)}; + } + + static Column TypedColumn(std::string col_name, int32_t col_index, + std::shared_ptr<DataType> type) { + return Column{std::move(col_name), col_index, false, std::move(type)}; + } + + static Column InferredColumn(std::string col_name, int32_t col_index) { + return Column{std::move(col_name), col_index, false, nullptr}; + } + + std::vector<Column> columns; +}; + +// An iterator of Buffers that makes sure there is no straddling CRLF sequence. +class CSVBufferIterator { + public: + static Iterator<std::shared_ptr<Buffer>> Make( + Iterator<std::shared_ptr<Buffer>> buffer_iterator) { + Transformer<std::shared_ptr<Buffer>, std::shared_ptr<Buffer>> fn = + CSVBufferIterator(); + return MakeTransformedIterator(std::move(buffer_iterator), fn); + } + + static AsyncGenerator<std::shared_ptr<Buffer>> MakeAsync( + AsyncGenerator<std::shared_ptr<Buffer>> buffer_iterator) { + Transformer<std::shared_ptr<Buffer>, std::shared_ptr<Buffer>> fn = + CSVBufferIterator(); + return MakeTransformedGenerator(std::move(buffer_iterator), fn); + } + + Result<TransformFlow<std::shared_ptr<Buffer>>> operator()(std::shared_ptr<Buffer> buf) { + if (buf == nullptr) { + // EOF + return TransformFinish(); + } + + int64_t offset = 0; + if (first_buffer_) { + ARROW_ASSIGN_OR_RAISE(auto data, util::SkipUTF8BOM(buf->data(), buf->size())); + offset += data - buf->data(); + DCHECK_GE(offset, 0); + first_buffer_ = false; + } + + if (trailing_cr_ && buf->data()[offset] == '\n') { + // Skip '\r\n' line separator that started at the end of previous buffer + ++offset; + } + + trailing_cr_ = (buf->data()[buf->size() - 1] == '\r'); + buf = SliceBuffer(buf, offset); + if (buf->size() == 0) { + // EOF + return TransformFinish(); + } else { + return TransformYield(buf); + } + } + + protected: + bool first_buffer_ = true; + // Whether there was a trailing CR at the end of last received buffer + bool trailing_cr_ = false; +}; + +struct CSVBlock { + // (partial + completion + buffer) is an entire delimited CSV buffer. + std::shared_ptr<Buffer> partial; + std::shared_ptr<Buffer> completion; + std::shared_ptr<Buffer> buffer; + int64_t block_index; + bool is_final; + int64_t bytes_skipped; + std::function<Status(int64_t)> consume_bytes; +}; + +} // namespace +} // namespace csv + +template <> +struct IterationTraits<csv::CSVBlock> { + static csv::CSVBlock End() { return csv::CSVBlock{{}, {}, {}, -1, true, 0, {}}; } + static bool IsEnd(const csv::CSVBlock& val) { return val.block_index < 0; } +}; + +namespace csv { +namespace { + +// This is a callable that can be used to transform an iterator. The source iterator +// will contain buffers of data and the output iterator will contain delimited CSV +// blocks. util::optional is used so that there is an end token (required by the +// iterator APIs (e.g. Visit)) even though an empty optional is never used in this code. +class BlockReader { + public: + BlockReader(std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, + int64_t skip_rows) + : chunker_(std::move(chunker)), + partial_(std::make_shared<Buffer>("")), + buffer_(std::move(first_buffer)), + skip_rows_(skip_rows) {} + + protected: + std::unique_ptr<Chunker> chunker_; + std::shared_ptr<Buffer> partial_, buffer_; + int64_t skip_rows_; + int64_t block_index_ = 0; + // Whether there was a trailing CR at the end of last received buffer + bool trailing_cr_ = false; +}; + +// An object that reads delimited CSV blocks for serial use. +// The number of bytes consumed should be notified after each read, +// using CSVBlock::consume_bytes. +class SerialBlockReader : public BlockReader { + public: + using BlockReader::BlockReader; + + static Iterator<CSVBlock> MakeIterator( + Iterator<std::shared_ptr<Buffer>> buffer_iterator, std::unique_ptr<Chunker> chunker, + std::shared_ptr<Buffer> first_buffer, int64_t skip_rows) { + auto block_reader = + std::make_shared<SerialBlockReader>(std::move(chunker), first_buffer, skip_rows); + // Wrap shared pointer in callable + Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = + [block_reader](std::shared_ptr<Buffer> buf) { + return (*block_reader)(std::move(buf)); + }; + return MakeTransformedIterator(std::move(buffer_iterator), block_reader_fn); + } + + static AsyncGenerator<CSVBlock> MakeAsyncIterator( + AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, + std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, + int64_t skip_rows) { + auto block_reader = + std::make_shared<SerialBlockReader>(std::move(chunker), first_buffer, skip_rows); + // Wrap shared pointer in callable + Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = + [block_reader](std::shared_ptr<Buffer> next) { + return (*block_reader)(std::move(next)); + }; + return MakeTransformedGenerator(std::move(buffer_generator), block_reader_fn); + } + + Result<TransformFlow<CSVBlock>> operator()(std::shared_ptr<Buffer> next_buffer) { + if (buffer_ == nullptr) { + return TransformFinish(); + } + + bool is_final = (next_buffer == nullptr); + int64_t bytes_skipped = 0; + + if (skip_rows_) { + bytes_skipped += partial_->size(); + auto orig_size = buffer_->size(); + RETURN_NOT_OK( + chunker_->ProcessSkip(partial_, buffer_, is_final, &skip_rows_, &buffer_)); + bytes_skipped += orig_size - buffer_->size(); + auto empty = std::make_shared<Buffer>(nullptr, 0); + if (skip_rows_) { + // Still have rows beyond this buffer to skip return empty block + partial_ = std::move(buffer_); + buffer_ = next_buffer; + return TransformYield<CSVBlock>(CSVBlock{empty, empty, empty, block_index_++, + is_final, bytes_skipped, + [](int64_t) { return Status::OK(); }}); + } + partial_ = std::move(empty); + } + + std::shared_ptr<Buffer> completion; + + if (is_final) { + // End of file reached => compute completion from penultimate block + RETURN_NOT_OK(chunker_->ProcessFinal(partial_, buffer_, &completion, &buffer_)); + } else { + // Get completion of partial from previous block. + RETURN_NOT_OK( + chunker_->ProcessWithPartial(partial_, buffer_, &completion, &buffer_)); + } + int64_t bytes_before_buffer = partial_->size() + completion->size(); + + auto consume_bytes = [this, bytes_before_buffer, + next_buffer](int64_t nbytes) -> Status { + DCHECK_GE(nbytes, 0); + auto offset = nbytes - bytes_before_buffer; + if (offset < 0) { + // Should not happen + return Status::Invalid("CSV parser got out of sync with chunker"); + } + partial_ = SliceBuffer(buffer_, offset); + buffer_ = next_buffer; + return Status::OK(); + }; + + return TransformYield<CSVBlock>(CSVBlock{partial_, completion, buffer_, + block_index_++, is_final, bytes_skipped, + std::move(consume_bytes)}); + } +}; + +// An object that reads delimited CSV blocks for threaded use. +class ThreadedBlockReader : public BlockReader { + public: + using BlockReader::BlockReader; + + static AsyncGenerator<CSVBlock> MakeAsyncIterator( + AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, + std::unique_ptr<Chunker> chunker, std::shared_ptr<Buffer> first_buffer, + int64_t skip_rows) { + auto block_reader = std::make_shared<ThreadedBlockReader>(std::move(chunker), + first_buffer, skip_rows); + // Wrap shared pointer in callable + Transformer<std::shared_ptr<Buffer>, CSVBlock> block_reader_fn = + [block_reader](std::shared_ptr<Buffer> next) { return (*block_reader)(next); }; + return MakeTransformedGenerator(std::move(buffer_generator), block_reader_fn); + } + + Result<TransformFlow<CSVBlock>> operator()(std::shared_ptr<Buffer> next_buffer) { + if (buffer_ == nullptr) { + // EOF + return TransformFinish(); + } + + bool is_final = (next_buffer == nullptr); + + auto current_partial = std::move(partial_); + auto current_buffer = std::move(buffer_); + int64_t bytes_skipped = 0; + + if (skip_rows_) { + auto orig_size = current_buffer->size(); + bytes_skipped = current_partial->size(); + RETURN_NOT_OK(chunker_->ProcessSkip(current_partial, current_buffer, is_final, + &skip_rows_, ¤t_buffer)); + bytes_skipped += orig_size - current_buffer->size(); + current_partial = std::make_shared<Buffer>(nullptr, 0); + if (skip_rows_) { + partial_ = std::move(current_buffer); + buffer_ = std::move(next_buffer); + return TransformYield<CSVBlock>(CSVBlock{current_partial, + current_partial, + current_partial, + block_index_++, + is_final, + bytes_skipped, + {}}); + } + } + + std::shared_ptr<Buffer> whole, completion, next_partial; + + if (is_final) { + // End of file reached => compute completion from penultimate block + RETURN_NOT_OK( + chunker_->ProcessFinal(current_partial, current_buffer, &completion, &whole)); + } else { + // Get completion of partial from previous block. + std::shared_ptr<Buffer> starts_with_whole; + // Get completion of partial from previous block. + RETURN_NOT_OK(chunker_->ProcessWithPartial(current_partial, current_buffer, + &completion, &starts_with_whole)); + + // Get a complete CSV block inside `partial + block`, and keep + // the rest for the next iteration. + RETURN_NOT_OK(chunker_->Process(starts_with_whole, &whole, &next_partial)); + } + + partial_ = std::move(next_partial); + buffer_ = std::move(next_buffer); + + return TransformYield<CSVBlock>(CSVBlock{ + current_partial, completion, whole, block_index_++, is_final, bytes_skipped, {}}); + } +}; + +struct ParsedBlock { + std::shared_ptr<BlockParser> parser; + int64_t block_index; + int64_t bytes_parsed_or_skipped; +}; + +struct DecodedBlock { + std::shared_ptr<RecordBatch> record_batch; + // Represents the number of input bytes represented by this batch + // This will include bytes skipped when skipping rows after the header + int64_t bytes_processed; +}; + +} // namespace + +} // namespace csv + +template <> +struct IterationTraits<csv::ParsedBlock> { + static csv::ParsedBlock End() { return csv::ParsedBlock{nullptr, -1, -1}; } + static bool IsEnd(const csv::ParsedBlock& val) { return val.block_index < 0; } +}; + +template <> +struct IterationTraits<csv::DecodedBlock> { + static csv::DecodedBlock End() { return csv::DecodedBlock{nullptr, -1}; } + static bool IsEnd(const csv::DecodedBlock& val) { return val.bytes_processed < 0; } +}; + +namespace csv { +namespace { + +// A function object that takes in a buffer of CSV data and returns a parsed batch of CSV +// data (CSVBlock -> ParsedBlock) for use with MakeMappedGenerator. +// The parsed batch contains a list of offsets for each of the columns so that columns +// can be individually scanned +// +// This operator is not re-entrant +class BlockParsingOperator { + public: + BlockParsingOperator(io::IOContext io_context, ParseOptions parse_options, + int num_csv_cols, int64_t first_row) + : io_context_(io_context), + parse_options_(parse_options), + num_csv_cols_(num_csv_cols), + count_rows_(first_row >= 0), + num_rows_seen_(first_row) {} + + Result<ParsedBlock> operator()(const CSVBlock& block) { + constexpr int32_t max_num_rows = std::numeric_limits<int32_t>::max(); + auto parser = std::make_shared<BlockParser>( + io_context_.pool(), parse_options_, num_csv_cols_, num_rows_seen_, max_num_rows); + + std::shared_ptr<Buffer> straddling; + std::vector<util::string_view> views; + if (block.partial->size() != 0 || block.completion->size() != 0) { + if (block.partial->size() == 0) { + straddling = block.completion; + } else if (block.completion->size() == 0) { + straddling = block.partial; + } else { + ARROW_ASSIGN_OR_RAISE( + straddling, + ConcatenateBuffers({block.partial, block.completion}, io_context_.pool())); + } + views = {util::string_view(*straddling), util::string_view(*block.buffer)}; + } else { + views = {util::string_view(*block.buffer)}; + } + uint32_t parsed_size; + if (block.is_final) { + RETURN_NOT_OK(parser->ParseFinal(views, &parsed_size)); + } else { + RETURN_NOT_OK(parser->Parse(views, &parsed_size)); + } + if (count_rows_) { + num_rows_seen_ += parser->num_rows(); + } + RETURN_NOT_OK(block.consume_bytes(parsed_size)); + return ParsedBlock{std::move(parser), block.block_index, + static_cast<int64_t>(parsed_size) + block.bytes_skipped}; + } + + private: + io::IOContext io_context_; + ParseOptions parse_options_; + int num_csv_cols_; + bool count_rows_; + int64_t num_rows_seen_; +}; + +// A function object that takes in parsed batch of CSV data and decodes it to an arrow +// record batch (ParsedBlock -> DecodedBlock) for use with MakeMappedGenerator. +class BlockDecodingOperator { + public: + Future<DecodedBlock> operator()(const ParsedBlock& block) { + DCHECK(!state_->column_decoders.empty()); + std::vector<Future<std::shared_ptr<Array>>> decoded_array_futs; + for (auto& decoder : state_->column_decoders) { + decoded_array_futs.push_back(decoder->Decode(block.parser)); + } + auto bytes_parsed_or_skipped = block.bytes_parsed_or_skipped; + auto decoded_arrays_fut = All(std::move(decoded_array_futs)); + auto state = state_; + return decoded_arrays_fut.Then( + [state, bytes_parsed_or_skipped]( + const std::vector<Result<std::shared_ptr<Array>>>& maybe_decoded_arrays) + -> Result<DecodedBlock> { + ARROW_ASSIGN_OR_RAISE(auto decoded_arrays, + internal::UnwrapOrRaise(maybe_decoded_arrays)); + + ARROW_ASSIGN_OR_RAISE(auto batch, + state->DecodedArraysToBatch(std::move(decoded_arrays))); + return DecodedBlock{std::move(batch), bytes_parsed_or_skipped}; + }); + } + + static Result<BlockDecodingOperator> Make(io::IOContext io_context, + ConvertOptions convert_options, + ConversionSchema conversion_schema) { + BlockDecodingOperator op(std::move(io_context), std::move(convert_options), + std::move(conversion_schema)); + RETURN_NOT_OK(op.state_->MakeColumnDecoders(io_context)); + return op; + } + + private: + BlockDecodingOperator(io::IOContext io_context, ConvertOptions convert_options, + ConversionSchema conversion_schema) + : state_(std::make_shared<State>(std::move(io_context), std::move(convert_options), + std::move(conversion_schema))) {} + + struct State { + State(io::IOContext io_context, ConvertOptions convert_options, + ConversionSchema conversion_schema) + : convert_options(std::move(convert_options)), + conversion_schema(std::move(conversion_schema)) {} + + Result<std::shared_ptr<RecordBatch>> DecodedArraysToBatch( + std::vector<std::shared_ptr<Array>> arrays) { + if (schema == nullptr) { + FieldVector fields(arrays.size()); + for (size_t i = 0; i < arrays.size(); ++i) { + fields[i] = field(conversion_schema.columns[i].name, arrays[i]->type()); + } + schema = arrow::schema(std::move(fields)); + } + const auto n_rows = arrays[0]->length(); + return RecordBatch::Make(schema, n_rows, std::move(arrays)); + } + + // Make column decoders from conversion schema + Status MakeColumnDecoders(io::IOContext io_context) { + for (const auto& column : conversion_schema.columns) { + std::shared_ptr<ColumnDecoder> decoder; + if (column.is_missing) { + ARROW_ASSIGN_OR_RAISE(decoder, + ColumnDecoder::MakeNull(io_context.pool(), column.type)); + } else if (column.type != nullptr) { + ARROW_ASSIGN_OR_RAISE( + decoder, ColumnDecoder::Make(io_context.pool(), column.type, column.index, + convert_options)); + } else { + ARROW_ASSIGN_OR_RAISE( + decoder, + ColumnDecoder::Make(io_context.pool(), column.index, convert_options)); + } + column_decoders.push_back(std::move(decoder)); + } + return Status::OK(); + } + + ConvertOptions convert_options; + ConversionSchema conversion_schema; + std::vector<std::shared_ptr<ColumnDecoder>> column_decoders; + std::shared_ptr<Schema> schema; + }; + + std::shared_ptr<State> state_; +}; + +///////////////////////////////////////////////////////////////////////// +// Base class for common functionality + +class ReaderMixin { + public: + ReaderMixin(io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options, bool count_rows) + : io_context_(std::move(io_context)), + read_options_(read_options), + parse_options_(parse_options), + convert_options_(convert_options), + count_rows_(count_rows), + num_rows_seen_(count_rows_ ? 1 : -1), + input_(std::move(input)) {} + + protected: + // Read header and column names from buffer, create column builders + // Returns the # of bytes consumed + Result<int64_t> ProcessHeader(const std::shared_ptr<Buffer>& buf, + std::shared_ptr<Buffer>* rest) { + const uint8_t* data = buf->data(); + const auto data_end = data + buf->size(); + DCHECK_GT(data_end - data, 0); + + if (read_options_.skip_rows) { + // Skip initial rows (potentially invalid CSV data) + auto num_skipped_rows = SkipRows(data, static_cast<uint32_t>(data_end - data), + read_options_.skip_rows, &data); + if (num_skipped_rows < read_options_.skip_rows) { + return Status::Invalid( + "Could not skip initial ", read_options_.skip_rows, + " rows from CSV file, " + "either file is too short or header is larger than block size"); + } + if (count_rows_) { + num_rows_seen_ += num_skipped_rows; + } + } + + if (read_options_.column_names.empty()) { + // Parse one row (either to read column names or to know the number of columns) + BlockParser parser(io_context_.pool(), parse_options_, num_csv_cols_, + num_rows_seen_, 1); + uint32_t parsed_size = 0; + RETURN_NOT_OK(parser.Parse( + util::string_view(reinterpret_cast<const char*>(data), data_end - data), + &parsed_size)); + if (parser.num_rows() != 1) { + return Status::Invalid( + "Could not read first row from CSV file, either " + "file is too short or header is larger than block size"); + } + if (parser.num_cols() == 0) { + return Status::Invalid("No columns in CSV file"); + } + + if (read_options_.autogenerate_column_names) { + column_names_ = GenerateColumnNames(parser.num_cols()); + } else { + // Read column names from header row + auto visit = [&](const uint8_t* data, uint32_t size, bool quoted) -> Status { + column_names_.emplace_back(reinterpret_cast<const char*>(data), size); + return Status::OK(); + }; + RETURN_NOT_OK(parser.VisitLastRow(visit)); + DCHECK_EQ(static_cast<size_t>(parser.num_cols()), column_names_.size()); + // Skip parsed header row + data += parsed_size; + if (count_rows_) { + ++num_rows_seen_; + } + } + } else { + column_names_ = read_options_.column_names; + } + + if (count_rows_) { + // increase rows seen to skip past rows which will be skipped + num_rows_seen_ += read_options_.skip_rows_after_names; + } + + auto bytes_consumed = data - buf->data(); + *rest = SliceBuffer(buf, bytes_consumed); + + num_csv_cols_ = static_cast<int32_t>(column_names_.size()); + DCHECK_GT(num_csv_cols_, 0); + + RETURN_NOT_OK(MakeConversionSchema()); + return bytes_consumed; + } + + std::vector<std::string> GenerateColumnNames(int32_t num_cols) { + std::vector<std::string> res; + res.reserve(num_cols); + for (int32_t i = 0; i < num_cols; ++i) { + std::stringstream ss; + ss << "f" << i; + res.push_back(ss.str()); + } + return res; + } + + // Make conversion schema from options and parsed CSV header + Status MakeConversionSchema() { + // Append a column converted from CSV data + auto append_csv_column = [&](std::string col_name, int32_t col_index) { + // Does the named column have a fixed type? + auto it = convert_options_.column_types.find(col_name); + if (it == convert_options_.column_types.end()) { + conversion_schema_.columns.push_back( + ConversionSchema::InferredColumn(std::move(col_name), col_index)); + } else { + conversion_schema_.columns.push_back( + ConversionSchema::TypedColumn(std::move(col_name), col_index, it->second)); + } + }; + + // Append a column of nulls + auto append_null_column = [&](std::string col_name) { + // If the named column has a fixed type, use it, otherwise use null() + std::shared_ptr<DataType> type; + auto it = convert_options_.column_types.find(col_name); + if (it == convert_options_.column_types.end()) { + type = null(); + } else { + type = it->second; + } + conversion_schema_.columns.push_back( + ConversionSchema::NullColumn(std::move(col_name), std::move(type))); + }; + + if (convert_options_.include_columns.empty()) { + // Include all columns in CSV file order + for (int32_t col_index = 0; col_index < num_csv_cols_; ++col_index) { + append_csv_column(column_names_[col_index], col_index); + } + } else { + // Include columns from `include_columns` (in that order) + // Compute indices of columns in the CSV file + std::unordered_map<std::string, int32_t> col_indices; + col_indices.reserve(column_names_.size()); + for (int32_t i = 0; i < static_cast<int32_t>(column_names_.size()); ++i) { + col_indices.emplace(column_names_[i], i); + } + + for (const auto& col_name : convert_options_.include_columns) { + auto it = col_indices.find(col_name); + if (it != col_indices.end()) { + append_csv_column(col_name, it->second); + } else if (convert_options_.include_missing_columns) { + append_null_column(col_name); + } else { + return Status::KeyError("Column '", col_name, + "' in include_columns " + "does not exist in CSV file"); + } + } + } + return Status::OK(); + } + + struct ParseResult { + std::shared_ptr<BlockParser> parser; + int64_t parsed_bytes; + }; + + Result<ParseResult> Parse(const std::shared_ptr<Buffer>& partial, + const std::shared_ptr<Buffer>& completion, + const std::shared_ptr<Buffer>& block, int64_t block_index, + bool is_final) { + static constexpr int32_t max_num_rows = std::numeric_limits<int32_t>::max(); + auto parser = std::make_shared<BlockParser>( + io_context_.pool(), parse_options_, num_csv_cols_, num_rows_seen_, max_num_rows); + + std::shared_ptr<Buffer> straddling; + std::vector<util::string_view> views; + if (partial->size() != 0 || completion->size() != 0) { + if (partial->size() == 0) { + straddling = completion; + } else if (completion->size() == 0) { + straddling = partial; + } else { + ARROW_ASSIGN_OR_RAISE( + straddling, ConcatenateBuffers({partial, completion}, io_context_.pool())); + } + views = {util::string_view(*straddling), util::string_view(*block)}; + } else { + views = {util::string_view(*block)}; + } + uint32_t parsed_size; + if (is_final) { + RETURN_NOT_OK(parser->ParseFinal(views, &parsed_size)); + } else { + RETURN_NOT_OK(parser->Parse(views, &parsed_size)); + } + if (count_rows_) { + num_rows_seen_ += parser->num_rows(); + } + return ParseResult{std::move(parser), static_cast<int64_t>(parsed_size)}; + } + + io::IOContext io_context_; + ReadOptions read_options_; + ParseOptions parse_options_; + ConvertOptions convert_options_; + + // Number of columns in the CSV file + int32_t num_csv_cols_ = -1; + // Whether num_rows_seen_ tracks the number of rows seen in the CSV being parsed + bool count_rows_; + // Number of rows seen in the csv. Not used if count_rows is false + int64_t num_rows_seen_; + // Column names in the CSV file + std::vector<std::string> column_names_; + ConversionSchema conversion_schema_; + + std::shared_ptr<io::InputStream> input_; + std::shared_ptr<internal::TaskGroup> task_group_; +}; + +///////////////////////////////////////////////////////////////////////// +// Base class for one-shot table readers + +class BaseTableReader : public ReaderMixin, public csv::TableReader { + public: + using ReaderMixin::ReaderMixin; + + virtual Status Init() = 0; + + Future<std::shared_ptr<Table>> ReadAsync() override { + return Future<std::shared_ptr<Table>>::MakeFinished(Read()); + } + + protected: + // Make column builders from conversion schema + Status MakeColumnBuilders() { + for (const auto& column : conversion_schema_.columns) { + std::shared_ptr<ColumnBuilder> builder; + if (column.is_missing) { + ARROW_ASSIGN_OR_RAISE(builder, ColumnBuilder::MakeNull(io_context_.pool(), + column.type, task_group_)); + } else if (column.type != nullptr) { + ARROW_ASSIGN_OR_RAISE( + builder, ColumnBuilder::Make(io_context_.pool(), column.type, column.index, + convert_options_, task_group_)); + } else { + ARROW_ASSIGN_OR_RAISE(builder, + ColumnBuilder::Make(io_context_.pool(), column.index, + convert_options_, task_group_)); + } + column_builders_.push_back(std::move(builder)); + } + return Status::OK(); + } + + Result<int64_t> ParseAndInsert(const std::shared_ptr<Buffer>& partial, + const std::shared_ptr<Buffer>& completion, + const std::shared_ptr<Buffer>& block, + int64_t block_index, bool is_final) { + ARROW_ASSIGN_OR_RAISE(auto result, + Parse(partial, completion, block, block_index, is_final)); + RETURN_NOT_OK(ProcessData(result.parser, block_index)); + return result.parsed_bytes; + } + + // Trigger conversion of parsed block data + Status ProcessData(const std::shared_ptr<BlockParser>& parser, int64_t block_index) { + for (auto& builder : column_builders_) { + builder->Insert(block_index, parser); + } + return Status::OK(); + } + + Result<std::shared_ptr<Table>> MakeTable() { + DCHECK_EQ(column_builders_.size(), conversion_schema_.columns.size()); + + std::vector<std::shared_ptr<Field>> fields; + std::vector<std::shared_ptr<ChunkedArray>> columns; + + for (int32_t i = 0; i < static_cast<int32_t>(column_builders_.size()); ++i) { + const auto& column = conversion_schema_.columns[i]; + ARROW_ASSIGN_OR_RAISE(auto array, column_builders_[i]->Finish()); + fields.push_back(::arrow::field(column.name, array->type())); + columns.emplace_back(std::move(array)); + } + return Table::Make(schema(std::move(fields)), std::move(columns)); + } + + // Column builders for target Table (in ConversionSchema order) + std::vector<std::shared_ptr<ColumnBuilder>> column_builders_; +}; + +///////////////////////////////////////////////////////////////////////// +// Base class for streaming readers + +class StreamingReaderImpl : public ReaderMixin, + public csv::StreamingReader, + public std::enable_shared_from_this<StreamingReaderImpl> { + public: + StreamingReaderImpl(io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options, bool count_rows) + : ReaderMixin(io_context, std::move(input), read_options, parse_options, + convert_options, count_rows), + bytes_decoded_(std::make_shared<std::atomic<int64_t>>(0)) {} + + Future<> Init(Executor* cpu_executor) { + ARROW_ASSIGN_OR_RAISE(auto istream_it, + io::MakeInputStreamIterator(input_, read_options_.block_size)); + + // TODO Consider exposing readahead as a read option (ARROW-12090) + ARROW_ASSIGN_OR_RAISE(auto bg_it, MakeBackgroundGenerator(std::move(istream_it), + io_context_.executor())); + + auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor); + + auto buffer_generator = CSVBufferIterator::MakeAsync(std::move(transferred_it)); + + int max_readahead = cpu_executor->GetCapacity(); + auto self = shared_from_this(); + + return buffer_generator().Then([self, buffer_generator, max_readahead]( + const std::shared_ptr<Buffer>& first_buffer) { + return self->InitAfterFirstBuffer(first_buffer, buffer_generator, max_readahead); + }); + } + + std::shared_ptr<Schema> schema() const override { return schema_; } + + int64_t bytes_read() const override { return bytes_decoded_->load(); } + + Status ReadNext(std::shared_ptr<RecordBatch>* batch) override { + auto next_fut = ReadNextAsync(); + auto next_result = next_fut.result(); + return std::move(next_result).Value(batch); + } + + Future<std::shared_ptr<RecordBatch>> ReadNextAsync() override { + return record_batch_gen_(); + } + + protected: + Future<> InitAfterFirstBuffer(const std::shared_ptr<Buffer>& first_buffer, + AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator, + int max_readahead) { + if (first_buffer == nullptr) { + return Status::Invalid("Empty CSV file"); + } + + std::shared_ptr<Buffer> after_header; + ARROW_ASSIGN_OR_RAISE(auto header_bytes_consumed, + ProcessHeader(first_buffer, &after_header)); + bytes_decoded_->fetch_add(header_bytes_consumed); + + auto parser_op = + BlockParsingOperator(io_context_, parse_options_, num_csv_cols_, num_rows_seen_); + ARROW_ASSIGN_OR_RAISE( + auto decoder_op, + BlockDecodingOperator::Make(io_context_, convert_options_, conversion_schema_)); + + auto block_gen = SerialBlockReader::MakeAsyncIterator( + std::move(buffer_generator), MakeChunker(parse_options_), std::move(after_header), + read_options_.skip_rows_after_names); + auto parsed_block_gen = + MakeMappedGenerator(std::move(block_gen), std::move(parser_op)); + auto rb_gen = MakeMappedGenerator(std::move(parsed_block_gen), std::move(decoder_op)); + + auto self = shared_from_this(); + return rb_gen().Then([self, rb_gen, max_readahead](const DecodedBlock& first_block) { + return self->InitAfterFirstBatch(first_block, std::move(rb_gen), max_readahead); + }); + } + + Status InitAfterFirstBatch(const DecodedBlock& first_block, + AsyncGenerator<DecodedBlock> batch_gen, int max_readahead) { + schema_ = first_block.record_batch->schema(); + + AsyncGenerator<DecodedBlock> readahead_gen; + if (read_options_.use_threads) { + readahead_gen = MakeReadaheadGenerator(std::move(batch_gen), max_readahead); + } else { + readahead_gen = std::move(batch_gen); + } + + AsyncGenerator<DecodedBlock> restarted_gen; + // Streaming reader should not emit empty record batches + if (first_block.record_batch->num_rows() > 0) { + restarted_gen = MakeGeneratorStartsWith({first_block}, std::move(readahead_gen)); + } else { + restarted_gen = std::move(readahead_gen); + } + + auto bytes_decoded = bytes_decoded_; + auto unwrap_and_record_bytes = + [bytes_decoded]( + const DecodedBlock& block) -> Result<std::shared_ptr<RecordBatch>> { + bytes_decoded->fetch_add(block.bytes_processed); + return block.record_batch; + }; + + auto unwrapped = + MakeMappedGenerator(std::move(restarted_gen), std::move(unwrap_and_record_bytes)); + + record_batch_gen_ = MakeCancellable(std::move(unwrapped), io_context_.stop_token()); + return Status::OK(); + } + + std::shared_ptr<Schema> schema_; + AsyncGenerator<std::shared_ptr<RecordBatch>> record_batch_gen_; + // bytes which have been decoded and asked for by the caller + std::shared_ptr<std::atomic<int64_t>> bytes_decoded_; +}; + +///////////////////////////////////////////////////////////////////////// +// Serial TableReader implementation + +class SerialTableReader : public BaseTableReader { + public: + using BaseTableReader::BaseTableReader; + + Status Init() override { + ARROW_ASSIGN_OR_RAISE(auto istream_it, + io::MakeInputStreamIterator(input_, read_options_.block_size)); + + // Since we're converting serially, no need to readahead more than one block + int32_t block_queue_size = 1; + ARROW_ASSIGN_OR_RAISE(auto rh_it, + MakeReadaheadIterator(std::move(istream_it), block_queue_size)); + buffer_iterator_ = CSVBufferIterator::Make(std::move(rh_it)); + return Status::OK(); + } + + Result<std::shared_ptr<Table>> Read() override { + task_group_ = internal::TaskGroup::MakeSerial(io_context_.stop_token()); + + // First block + ARROW_ASSIGN_OR_RAISE(auto first_buffer, buffer_iterator_.Next()); + if (first_buffer == nullptr) { + return Status::Invalid("Empty CSV file"); + } + RETURN_NOT_OK(ProcessHeader(first_buffer, &first_buffer)); + RETURN_NOT_OK(MakeColumnBuilders()); + + auto block_iterator = SerialBlockReader::MakeIterator( + std::move(buffer_iterator_), MakeChunker(parse_options_), std::move(first_buffer), + read_options_.skip_rows_after_names); + while (true) { + RETURN_NOT_OK(io_context_.stop_token().Poll()); + + ARROW_ASSIGN_OR_RAISE(auto maybe_block, block_iterator.Next()); + if (IsIterationEnd(maybe_block)) { + // EOF + break; + } + ARROW_ASSIGN_OR_RAISE( + int64_t parsed_bytes, + ParseAndInsert(maybe_block.partial, maybe_block.completion, maybe_block.buffer, + maybe_block.block_index, maybe_block.is_final)); + RETURN_NOT_OK(maybe_block.consume_bytes(parsed_bytes)); + } + // Finish conversion, create schema and table + RETURN_NOT_OK(task_group_->Finish()); + return MakeTable(); + } + + protected: + Iterator<std::shared_ptr<Buffer>> buffer_iterator_; +}; + +class AsyncThreadedTableReader + : public BaseTableReader, + public std::enable_shared_from_this<AsyncThreadedTableReader> { + public: + using BaseTableReader::BaseTableReader; + + AsyncThreadedTableReader(io::IOContext io_context, + std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, + const ParseOptions& parse_options, + const ConvertOptions& convert_options, Executor* cpu_executor) + // Count rows is currently not supported during parallel read + : BaseTableReader(std::move(io_context), input, read_options, parse_options, + convert_options, /*count_rows=*/false), + cpu_executor_(cpu_executor) {} + + ~AsyncThreadedTableReader() override { + if (task_group_) { + // In case of error, make sure all pending tasks are finished before + // we start destroying BaseTableReader members + ARROW_UNUSED(task_group_->Finish()); + } + } + + Status Init() override { + ARROW_ASSIGN_OR_RAISE(auto istream_it, + io::MakeInputStreamIterator(input_, read_options_.block_size)); + + int max_readahead = cpu_executor_->GetCapacity(); + int readahead_restart = std::max(1, max_readahead / 2); + + ARROW_ASSIGN_OR_RAISE( + auto bg_it, MakeBackgroundGenerator(std::move(istream_it), io_context_.executor(), + max_readahead, readahead_restart)); + + auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor_); + buffer_generator_ = CSVBufferIterator::MakeAsync(std::move(transferred_it)); + return Status::OK(); + } + + Result<std::shared_ptr<Table>> Read() override { return ReadAsync().result(); } + + Future<std::shared_ptr<Table>> ReadAsync() override { + task_group_ = + internal::TaskGroup::MakeThreaded(cpu_executor_, io_context_.stop_token()); + + auto self = shared_from_this(); + return ProcessFirstBuffer().Then([self](const std::shared_ptr<Buffer>& first_buffer) { + auto block_generator = ThreadedBlockReader::MakeAsyncIterator( + self->buffer_generator_, MakeChunker(self->parse_options_), + std::move(first_buffer), self->read_options_.skip_rows_after_names); + + std::function<Status(CSVBlock)> block_visitor = + [self](CSVBlock maybe_block) -> Status { + // The logic in VisitAsyncGenerator ensures that we will never be + // passed an empty block (visit does not call with the end token) so + // we can be assured maybe_block has a value. + DCHECK_GE(maybe_block.block_index, 0); + DCHECK(!maybe_block.consume_bytes); + + // Launch parse task + self->task_group_->Append([self, maybe_block] { + return self + ->ParseAndInsert(maybe_block.partial, maybe_block.completion, + maybe_block.buffer, maybe_block.block_index, + maybe_block.is_final) + .status(); + }); + return Status::OK(); + }; + + return VisitAsyncGenerator(std::move(block_generator), block_visitor) + .Then([self]() -> Future<> { + // By this point we've added all top level tasks so it is safe to call + // FinishAsync + return self->task_group_->FinishAsync(); + }) + .Then([self]() -> Result<std::shared_ptr<Table>> { + // Finish conversion, create schema and table + return self->MakeTable(); + }); + }); + } + + protected: + Future<std::shared_ptr<Buffer>> ProcessFirstBuffer() { + // First block + auto first_buffer_future = buffer_generator_(); + return first_buffer_future.Then([this](const std::shared_ptr<Buffer>& first_buffer) + -> Result<std::shared_ptr<Buffer>> { + if (first_buffer == nullptr) { + return Status::Invalid("Empty CSV file"); + } + std::shared_ptr<Buffer> first_buffer_processed; + RETURN_NOT_OK(ProcessHeader(first_buffer, &first_buffer_processed)); + RETURN_NOT_OK(MakeColumnBuilders()); + return first_buffer_processed; + }); + } + + Executor* cpu_executor_; + AsyncGenerator<std::shared_ptr<Buffer>> buffer_generator_; +}; + +Result<std::shared_ptr<TableReader>> MakeTableReader( + MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options) { + RETURN_NOT_OK(parse_options.Validate()); + RETURN_NOT_OK(read_options.Validate()); + RETURN_NOT_OK(convert_options.Validate()); + std::shared_ptr<BaseTableReader> reader; + if (read_options.use_threads) { + auto cpu_executor = internal::GetCpuThreadPool(); + reader = std::make_shared<AsyncThreadedTableReader>( + io_context, input, read_options, parse_options, convert_options, cpu_executor); + } else { + reader = std::make_shared<SerialTableReader>(io_context, input, read_options, + parse_options, convert_options, + /*count_rows=*/true); + } + RETURN_NOT_OK(reader->Init()); + return reader; +} + +Future<std::shared_ptr<StreamingReader>> MakeStreamingReader( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + internal::Executor* cpu_executor, const ReadOptions& read_options, + const ParseOptions& parse_options, const ConvertOptions& convert_options) { + RETURN_NOT_OK(parse_options.Validate()); + RETURN_NOT_OK(read_options.Validate()); + RETURN_NOT_OK(convert_options.Validate()); + std::shared_ptr<StreamingReaderImpl> reader; + reader = std::make_shared<StreamingReaderImpl>( + io_context, input, read_options, parse_options, convert_options, + /*count_rows=*/!read_options.use_threads || cpu_executor->GetCapacity() == 1); + return reader->Init(cpu_executor).Then([reader] { + return std::dynamic_pointer_cast<StreamingReader>(reader); + }); +} + +///////////////////////////////////////////////////////////////////////// +// Row count implementation + +class CSVRowCounter : public ReaderMixin, + public std::enable_shared_from_this<CSVRowCounter> { + public: + CSVRowCounter(io::IOContext io_context, Executor* cpu_executor, + std::shared_ptr<io::InputStream> input, const ReadOptions& read_options, + const ParseOptions& parse_options) + : ReaderMixin(io_context, std::move(input), read_options, parse_options, + ConvertOptions::Defaults(), /*count_rows=*/true), + cpu_executor_(cpu_executor), + row_count_(0) {} + + Future<int64_t> Count() { + auto self = shared_from_this(); + return Init(self).Then([self]() { return self->DoCount(self); }); + } + + private: + Future<> Init(const std::shared_ptr<CSVRowCounter>& self) { + ARROW_ASSIGN_OR_RAISE(auto istream_it, + io::MakeInputStreamIterator(input_, read_options_.block_size)); + // TODO Consider exposing readahead as a read option (ARROW-12090) + ARROW_ASSIGN_OR_RAISE(auto bg_it, MakeBackgroundGenerator(std::move(istream_it), + io_context_.executor())); + auto transferred_it = MakeTransferredGenerator(bg_it, cpu_executor_); + auto buffer_generator = CSVBufferIterator::MakeAsync(std::move(transferred_it)); + + return buffer_generator().Then( + [self, buffer_generator](std::shared_ptr<Buffer> first_buffer) { + if (!first_buffer) { + return Status::Invalid("Empty CSV file"); + } + RETURN_NOT_OK(self->ProcessHeader(first_buffer, &first_buffer)); + self->block_generator_ = SerialBlockReader::MakeAsyncIterator( + buffer_generator, MakeChunker(self->parse_options_), + std::move(first_buffer), 0); + return Status::OK(); + }); + } + + Future<int64_t> DoCount(const std::shared_ptr<CSVRowCounter>& self) { + // count_cb must return a value instead of Status/Future<> to work with + // MakeMappedGenerator, and it must use a type with a valid end value to work with + // IterationEnd. + std::function<Result<util::optional<int64_t>>(const CSVBlock&)> count_cb = + [self](const CSVBlock& maybe_block) -> Result<util::optional<int64_t>> { + ARROW_ASSIGN_OR_RAISE( + auto parser, + self->Parse(maybe_block.partial, maybe_block.completion, maybe_block.buffer, + maybe_block.block_index, maybe_block.is_final)); + RETURN_NOT_OK(maybe_block.consume_bytes(parser.parsed_bytes)); + self->row_count_ += parser.parser->num_rows(); + return parser.parser->num_rows(); + }; + auto count_gen = MakeMappedGenerator(block_generator_, std::move(count_cb)); + return DiscardAllFromAsyncGenerator(count_gen).Then( + [self]() { return self->row_count_; }); + } + + Executor* cpu_executor_; + AsyncGenerator<CSVBlock> block_generator_; + int64_t row_count_; +}; + +} // namespace + +///////////////////////////////////////////////////////////////////////// +// Factory functions + +Result<std::shared_ptr<TableReader>> TableReader::Make( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options) { + return MakeTableReader(io_context.pool(), io_context, std::move(input), read_options, + parse_options, convert_options); +} + +Result<std::shared_ptr<TableReader>> TableReader::Make( + MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options) { + return MakeTableReader(pool, io_context, std::move(input), read_options, parse_options, + convert_options); +} + +Result<std::shared_ptr<StreamingReader>> StreamingReader::Make( + MemoryPool* pool, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options) { + auto io_context = io::IOContext(pool); + auto cpu_executor = internal::GetCpuThreadPool(); + auto reader_fut = MakeStreamingReader(io_context, std::move(input), cpu_executor, + read_options, parse_options, convert_options); + auto reader_result = reader_fut.result(); + ARROW_ASSIGN_OR_RAISE(auto reader, reader_result); + return reader; +} + +Result<std::shared_ptr<StreamingReader>> StreamingReader::Make( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options) { + auto cpu_executor = internal::GetCpuThreadPool(); + auto reader_fut = MakeStreamingReader(io_context, std::move(input), cpu_executor, + read_options, parse_options, convert_options); + auto reader_result = reader_fut.result(); + ARROW_ASSIGN_OR_RAISE(auto reader, reader_result); + return reader; +} + +Future<std::shared_ptr<StreamingReader>> StreamingReader::MakeAsync( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + internal::Executor* cpu_executor, const ReadOptions& read_options, + const ParseOptions& parse_options, const ConvertOptions& convert_options) { + return MakeStreamingReader(io_context, std::move(input), cpu_executor, read_options, + parse_options, convert_options); +} + +Future<int64_t> CountRowsAsync(io::IOContext io_context, + std::shared_ptr<io::InputStream> input, + internal::Executor* cpu_executor, + const ReadOptions& read_options, + const ParseOptions& parse_options) { + RETURN_NOT_OK(parse_options.Validate()); + RETURN_NOT_OK(read_options.Validate()); + auto counter = std::make_shared<CSVRowCounter>( + io_context, cpu_executor, std::move(input), read_options, parse_options); + return counter->Count(); +} + +} // namespace csv + +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.h index b1c1749f4b..48f02882b1 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/reader.h @@ -1,123 +1,123 @@ -// 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 <memory> - -#include "arrow/csv/options.h" // IWYU pragma: keep -#include "arrow/io/interfaces.h" -#include "arrow/record_batch.h" -#include "arrow/result.h" -#include "arrow/type.h" -#include "arrow/type_fwd.h" -#include "arrow/util/future.h" -#include "arrow/util/thread_pool.h" -#include "arrow/util/visibility.h" - -namespace arrow { -namespace io { -class InputStream; -} // namespace io - -namespace csv { - -/// A class that reads an entire CSV file into a Arrow Table -class ARROW_EXPORT TableReader { - public: - virtual ~TableReader() = default; - - /// Read the entire CSV file and convert it to a Arrow Table - virtual Result<std::shared_ptr<Table>> Read() = 0; - /// Read the entire CSV file and convert it to a Arrow Table - virtual Future<std::shared_ptr<Table>> ReadAsync() = 0; - - /// Create a TableReader instance - static Result<std::shared_ptr<TableReader>> Make(io::IOContext io_context, - std::shared_ptr<io::InputStream> input, - const ReadOptions&, - const ParseOptions&, - const ConvertOptions&); - - ARROW_DEPRECATED("Use MemoryPool-less variant (the IOContext holds a pool already)") - static Result<std::shared_ptr<TableReader>> Make( - MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions&, const ParseOptions&, const ConvertOptions&); -}; - -/// \brief A class that reads a CSV file incrementally -/// -/// Caveats: -/// - For now, this is always single-threaded (regardless of `ReadOptions::use_threads`. -/// - Type inference is done on the first block and types are frozen afterwards; -/// to make sure the right data types are inferred, either set -/// `ReadOptions::block_size` to a large enough value, or use -/// `ConvertOptions::column_types` to set the desired data types explicitly. -class ARROW_EXPORT StreamingReader : public RecordBatchReader { - public: - virtual ~StreamingReader() = default; - - virtual Future<std::shared_ptr<RecordBatch>> ReadNextAsync() = 0; - - /// \brief Return the number of bytes which have been read and processed - /// - /// The returned number includes CSV bytes which the StreamingReader has - /// finished processing, but not bytes for which some processing (e.g. - /// CSV parsing or conversion to Arrow layout) is still ongoing. - /// - /// Furthermore, the following rules apply: - /// - bytes skipped by `ReadOptions.skip_rows` are counted as being read before - /// any records are returned. - /// - bytes read while parsing the header are counted as being read before any - /// records are returned. - /// - bytes skipped by `ReadOptions.skip_rows_after_names` are counted after the - /// first batch is returned. - virtual int64_t bytes_read() const = 0; - - /// Create a StreamingReader instance - /// - /// This involves some I/O as the first batch must be loaded during the creation process - /// so it is returned as a future - /// - /// Currently, the StreamingReader is not async-reentrant and does not do any fan-out - /// parsing (see ARROW-11889) - static Future<std::shared_ptr<StreamingReader>> MakeAsync( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - internal::Executor* cpu_executor, const ReadOptions&, const ParseOptions&, - const ConvertOptions&); - - static Result<std::shared_ptr<StreamingReader>> Make( - io::IOContext io_context, std::shared_ptr<io::InputStream> input, - const ReadOptions&, const ParseOptions&, const ConvertOptions&); - - ARROW_DEPRECATED("Use IOContext-based overload") - static Result<std::shared_ptr<StreamingReader>> Make( - MemoryPool* pool, std::shared_ptr<io::InputStream> input, - const ReadOptions& read_options, const ParseOptions& parse_options, - const ConvertOptions& convert_options); -}; - -/// \brief Count the logical rows of data in a CSV file (i.e. the -/// number of rows you would get if you read the file into a table). -ARROW_EXPORT -Future<int64_t> CountRowsAsync(io::IOContext io_context, - std::shared_ptr<io::InputStream> input, - internal::Executor* cpu_executor, const ReadOptions&, - const ParseOptions&); - -} // namespace csv -} // namespace arrow +// 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 <memory> + +#include "arrow/csv/options.h" // IWYU pragma: keep +#include "arrow/io/interfaces.h" +#include "arrow/record_batch.h" +#include "arrow/result.h" +#include "arrow/type.h" +#include "arrow/type_fwd.h" +#include "arrow/util/future.h" +#include "arrow/util/thread_pool.h" +#include "arrow/util/visibility.h" + +namespace arrow { +namespace io { +class InputStream; +} // namespace io + +namespace csv { + +/// A class that reads an entire CSV file into a Arrow Table +class ARROW_EXPORT TableReader { + public: + virtual ~TableReader() = default; + + /// Read the entire CSV file and convert it to a Arrow Table + virtual Result<std::shared_ptr<Table>> Read() = 0; + /// Read the entire CSV file and convert it to a Arrow Table + virtual Future<std::shared_ptr<Table>> ReadAsync() = 0; + + /// Create a TableReader instance + static Result<std::shared_ptr<TableReader>> Make(io::IOContext io_context, + std::shared_ptr<io::InputStream> input, + const ReadOptions&, + const ParseOptions&, + const ConvertOptions&); + + ARROW_DEPRECATED("Use MemoryPool-less variant (the IOContext holds a pool already)") + static Result<std::shared_ptr<TableReader>> Make( + MemoryPool* pool, io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions&, const ParseOptions&, const ConvertOptions&); +}; + +/// \brief A class that reads a CSV file incrementally +/// +/// Caveats: +/// - For now, this is always single-threaded (regardless of `ReadOptions::use_threads`. +/// - Type inference is done on the first block and types are frozen afterwards; +/// to make sure the right data types are inferred, either set +/// `ReadOptions::block_size` to a large enough value, or use +/// `ConvertOptions::column_types` to set the desired data types explicitly. +class ARROW_EXPORT StreamingReader : public RecordBatchReader { + public: + virtual ~StreamingReader() = default; + + virtual Future<std::shared_ptr<RecordBatch>> ReadNextAsync() = 0; + + /// \brief Return the number of bytes which have been read and processed + /// + /// The returned number includes CSV bytes which the StreamingReader has + /// finished processing, but not bytes for which some processing (e.g. + /// CSV parsing or conversion to Arrow layout) is still ongoing. + /// + /// Furthermore, the following rules apply: + /// - bytes skipped by `ReadOptions.skip_rows` are counted as being read before + /// any records are returned. + /// - bytes read while parsing the header are counted as being read before any + /// records are returned. + /// - bytes skipped by `ReadOptions.skip_rows_after_names` are counted after the + /// first batch is returned. + virtual int64_t bytes_read() const = 0; + + /// Create a StreamingReader instance + /// + /// This involves some I/O as the first batch must be loaded during the creation process + /// so it is returned as a future + /// + /// Currently, the StreamingReader is not async-reentrant and does not do any fan-out + /// parsing (see ARROW-11889) + static Future<std::shared_ptr<StreamingReader>> MakeAsync( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + internal::Executor* cpu_executor, const ReadOptions&, const ParseOptions&, + const ConvertOptions&); + + static Result<std::shared_ptr<StreamingReader>> Make( + io::IOContext io_context, std::shared_ptr<io::InputStream> input, + const ReadOptions&, const ParseOptions&, const ConvertOptions&); + + ARROW_DEPRECATED("Use IOContext-based overload") + static Result<std::shared_ptr<StreamingReader>> Make( + MemoryPool* pool, std::shared_ptr<io::InputStream> input, + const ReadOptions& read_options, const ParseOptions& parse_options, + const ConvertOptions& convert_options); +}; + +/// \brief Count the logical rows of data in a CSV file (i.e. the +/// number of rows you would get if you read the file into a table). +ARROW_EXPORT +Future<int64_t> CountRowsAsync(io::IOContext io_context, + std::shared_ptr<io::InputStream> input, + internal::Executor* cpu_executor, const ReadOptions&, + const ParseOptions&); + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/type_fwd.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/type_fwd.h index e34a1ab7f5..c0a53847a9 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/type_fwd.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/type_fwd.h @@ -1,28 +1,28 @@ -// 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. - -namespace arrow { -namespace csv { - -class TableReader; -struct ConvertOptions; -struct ReadOptions; -struct ParseOptions; -struct WriteOptions; - -} // namespace csv -} // namespace arrow +// 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. + +namespace arrow { +namespace csv { + +class TableReader; +struct ConvertOptions; +struct ReadOptions; +struct ParseOptions; +struct WriteOptions; + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.cc b/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.cc index ac58350221..1b782cae7d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.cc @@ -1,460 +1,460 @@ -// 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. - -#include "arrow/csv/writer.h" -#include "arrow/array.h" -#include "arrow/compute/cast.h" -#include "arrow/io/interfaces.h" -#include "arrow/ipc/writer.h" -#include "arrow/record_batch.h" -#include "arrow/result.h" -#include "arrow/result_internal.h" -#include "arrow/stl_allocator.h" -#include "arrow/util/iterator.h" -#include "arrow/util/logging.h" -#include "arrow/util/make_unique.h" - -#include "arrow/visitor_inline.h" - -namespace arrow { -namespace csv { -// This implementation is intentionally light on configurability to minimize the size of -// the initial PR. Aditional features can be added as there is demand and interest to -// implement them. -// -// The algorithm used here at a high level is to break RecordBatches/Tables into slices -// and convert each slice independently. A slice is then converted to CSV by first -// scanning each column to determine the size of its contents when rendered as a string in -// CSV. For non-string types this requires casting the value to string (which is cached). -// This data is used to understand the precise length of each row and a single allocation -// for the final CSV data buffer. Once the final size is known each column is then -// iterated over again to place its contents into the CSV data buffer. The rationale for -// choosing this approach is it allows for reuse of the cast functionality in the compute -// module and inline data visiting functionality in the core library. A performance -// comparison has not been done using a naive single-pass approach. This approach might -// still be competitive due to reduction in the number of per row branches necessary with -// a single pass approach. Profiling would likely yield further opportunities for -// optimization with this approach. - -namespace { - -struct SliceIteratorFunctor { - Result<std::shared_ptr<RecordBatch>> Next() { - if (current_offset < batch->num_rows()) { - std::shared_ptr<RecordBatch> next = batch->Slice(current_offset, slice_size); - current_offset += slice_size; - return next; - } - return IterationTraits<std::shared_ptr<RecordBatch>>::End(); - } - const RecordBatch* const batch; - const int64_t slice_size; - int64_t current_offset; -}; - -RecordBatchIterator RecordBatchSliceIterator(const RecordBatch& batch, - int64_t slice_size) { - SliceIteratorFunctor functor = {&batch, slice_size, /*offset=*/static_cast<int64_t>(0)}; - return RecordBatchIterator(std::move(functor)); -} - -// Counts the number of characters that need escaping in s. -int64_t CountEscapes(util::string_view s) { - return static_cast<int64_t>(std::count(s.begin(), s.end(), '"')); -} - -// Matching quote pair character length. -constexpr int64_t kQuoteCount = 2; -constexpr int64_t kQuoteDelimiterCount = kQuoteCount + /*end_char*/ 1; - -// Interface for generating CSV data per column. -// The intended usage is to iteratively call UpdateRowLengths for a column and -// then PopulateColumns. PopulateColumns must be called in the reverse order of the -// populators (it populates data backwards). -class ColumnPopulator { - public: - ColumnPopulator(MemoryPool* pool, char end_char) : end_char_(end_char), pool_(pool) {} - - virtual ~ColumnPopulator() = default; - - // Adds the number of characters each entry in data will add to to elements - // in row_lengths. - Status UpdateRowLengths(const Array& data, int32_t* row_lengths) { - compute::ExecContext ctx(pool_); - // Populators are intented to be applied to reasonably small data. In most cases - // threading overhead would not be justified. - ctx.set_use_threads(false); - ASSIGN_OR_RAISE( - std::shared_ptr<Array> casted, - compute::Cast(data, /*to_type=*/utf8(), compute::CastOptions(), &ctx)); - casted_array_ = internal::checked_pointer_cast<StringArray>(casted); - return UpdateRowLengths(row_lengths); - } - - // Places string data onto each row in output and updates the corresponding row - // row pointers in preparation for calls to other (preceding) ColumnPopulators. - // Args: - // output: character buffer to write to. - // offsets: an array of end of row column within the the output buffer (values are - // one past the end of the position to write to). - virtual void PopulateColumns(char* output, int32_t* offsets) const = 0; - - protected: - virtual Status UpdateRowLengths(int32_t* row_lengths) = 0; - std::shared_ptr<StringArray> casted_array_; - const char end_char_; - - private: - MemoryPool* const pool_; -}; - -// Copies the contents of to out properly escaping any necessary characters. -// Returns the position prior to last copied character (out_end is decremented). -char* EscapeReverse(arrow::util::string_view s, char* out_end) { - for (const char* val = s.data() + s.length() - 1; val >= s.data(); val--, out_end--) { - if (*val == '"') { - *out_end = *val; - out_end--; - } - *out_end = *val; - } - return out_end; -} - -// Populator for non-string types. This populator relies on compute Cast functionality to -// String if it doesn't exist it will be an error. it also assumes the resulting string -// from a cast does not require quoting or escaping. -class UnquotedColumnPopulator : public ColumnPopulator { - public: - explicit UnquotedColumnPopulator(MemoryPool* memory_pool, char end_char) - : ColumnPopulator(memory_pool, end_char) {} - - Status UpdateRowLengths(int32_t* row_lengths) override { - for (int x = 0; x < casted_array_->length(); x++) { - row_lengths[x] += casted_array_->value_length(x); - } - return Status::OK(); - } - - void PopulateColumns(char* output, int32_t* offsets) const override { - VisitArrayDataInline<StringType>( - *casted_array_->data(), - [&](arrow::util::string_view s) { - int64_t next_column_offset = s.length() + /*end_char*/ 1; - memcpy((output + *offsets - next_column_offset), s.data(), s.length()); - *(output + *offsets - 1) = end_char_; - *offsets -= static_cast<int32_t>(next_column_offset); - offsets++; - }, - [&]() { - // Nulls are empty (unquoted) to distinguish with empty string. - *(output + *offsets - 1) = end_char_; - *offsets -= 1; - offsets++; - }); - } -}; - -// Strings need special handling to ensure they are escaped properly. -// This class handles escaping assuming that all strings will be quoted -// and that the only character within the string that needs to escaped is -// a quote character (") and escaping is done my adding another quote. -class QuotedColumnPopulator : public ColumnPopulator { - public: - QuotedColumnPopulator(MemoryPool* pool, char end_char) - : ColumnPopulator(pool, end_char) {} - - Status UpdateRowLengths(int32_t* row_lengths) override { - const StringArray& input = *casted_array_; - int row_number = 0; - row_needs_escaping_.resize(casted_array_->length()); - VisitArrayDataInline<StringType>( - *input.data(), - [&](arrow::util::string_view s) { - int64_t escaped_count = CountEscapes(s); - // TODO: Maybe use 64 bit row lengths or safe cast? - row_needs_escaping_[row_number] = escaped_count > 0; - row_lengths[row_number] += static_cast<int32_t>(s.length()) + - static_cast<int32_t>(escaped_count + kQuoteCount); - row_number++; - }, - [&]() { - row_needs_escaping_[row_number] = false; - row_number++; - }); - return Status::OK(); - } - - void PopulateColumns(char* output, int32_t* offsets) const override { - auto needs_escaping = row_needs_escaping_.begin(); - VisitArrayDataInline<StringType>( - *(casted_array_->data()), - [&](arrow::util::string_view s) { - // still needs string content length to be added - char* row_end = output + *offsets; - int32_t next_column_offset = 0; - if (!*needs_escaping) { - next_column_offset = static_cast<int32_t>(s.length() + kQuoteDelimiterCount); - memcpy(row_end - next_column_offset + /*quote_offset=*/1, s.data(), - s.length()); - } else { - // Adjust row_end by 3: 1 quote char, 1 end char and 1 to position at the - // first position to write to. - next_column_offset = - static_cast<int32_t>(row_end - EscapeReverse(s, row_end - 3)); - } - *(row_end - next_column_offset) = '"'; - *(row_end - 2) = '"'; - *(row_end - 1) = end_char_; - *offsets -= next_column_offset; - offsets++; - needs_escaping++; - }, - [&]() { - // Nulls are empty (unquoted) to distinguish with empty string. - *(output + *offsets - 1) = end_char_; - *offsets -= 1; - offsets++; - needs_escaping++; - }); - } - - private: - // Older version of GCC don't support custom allocators - // at some point we should change this to use memory_pool - // backed allocator. - std::vector<bool> row_needs_escaping_; -}; - -struct PopulatorFactory { - template <typename TypeClass> - enable_if_t<is_base_binary_type<TypeClass>::value || - std::is_same<FixedSizeBinaryType, TypeClass>::value, - Status> - Visit(const TypeClass& type) { - populator = new QuotedColumnPopulator(pool, end_char); - return Status::OK(); - } - - template <typename TypeClass> - enable_if_dictionary<TypeClass, Status> Visit(const TypeClass& type) { - return VisitTypeInline(*type.value_type(), this); - } - - template <typename TypeClass> - enable_if_t<is_nested_type<TypeClass>::value || is_extension_type<TypeClass>::value, - Status> - Visit(const TypeClass& type) { - return Status::Invalid("Unsupported Type:", type.ToString()); - } - - template <typename TypeClass> - enable_if_t<is_primitive_ctype<TypeClass>::value || is_decimal_type<TypeClass>::value || - is_null_type<TypeClass>::value || is_temporal_type<TypeClass>::value, - Status> - Visit(const TypeClass& type) { - populator = new UnquotedColumnPopulator(pool, end_char); - return Status::OK(); - } - - char end_char; - MemoryPool* pool; - ColumnPopulator* populator; -}; - -Result<std::unique_ptr<ColumnPopulator>> MakePopulator(const Field& field, char end_char, - MemoryPool* pool) { - PopulatorFactory factory{end_char, pool, nullptr}; - RETURN_NOT_OK(VisitTypeInline(*field.type(), &factory)); - return std::unique_ptr<ColumnPopulator>(factory.populator); -} - -class CSVWriterImpl : public ipc::RecordBatchWriter { - public: - static Result<std::shared_ptr<CSVWriterImpl>> Make( - io::OutputStream* sink, std::shared_ptr<io::OutputStream> owned_sink, - std::shared_ptr<Schema> schema, const WriteOptions& options) { - RETURN_NOT_OK(options.Validate()); - std::vector<std::unique_ptr<ColumnPopulator>> populators(schema->num_fields()); - for (int col = 0; col < schema->num_fields(); col++) { - char end_char = col < schema->num_fields() - 1 ? ',' : '\n'; - ASSIGN_OR_RAISE(populators[col], MakePopulator(*schema->field(col), end_char, - options.io_context.pool())); - } - auto writer = std::make_shared<CSVWriterImpl>( - sink, std::move(owned_sink), std::move(schema), std::move(populators), options); - RETURN_NOT_OK(writer->PrepareForContentsWrite()); - if (options.include_header) { - RETURN_NOT_OK(writer->WriteHeader()); - } - return writer; - } - - Status WriteRecordBatch(const RecordBatch& batch) override { - RecordBatchIterator iterator = RecordBatchSliceIterator(batch, options_.batch_size); - for (auto maybe_slice : iterator) { - ASSIGN_OR_RAISE(std::shared_ptr<RecordBatch> slice, maybe_slice); - RETURN_NOT_OK(TranslateMinimalBatch(*slice)); - RETURN_NOT_OK(sink_->Write(data_buffer_)); - stats_.num_record_batches++; - } - return Status::OK(); - } - - Status WriteTable(const Table& table, int64_t max_chunksize) override { - TableBatchReader reader(table); - reader.set_chunksize(max_chunksize > 0 ? max_chunksize : options_.batch_size); - std::shared_ptr<RecordBatch> batch; - RETURN_NOT_OK(reader.ReadNext(&batch)); - while (batch != nullptr) { - RETURN_NOT_OK(TranslateMinimalBatch(*batch)); - RETURN_NOT_OK(sink_->Write(data_buffer_)); - RETURN_NOT_OK(reader.ReadNext(&batch)); - stats_.num_record_batches++; - } - - return Status::OK(); - } - - Status Close() override { return Status::OK(); } - - ipc::WriteStats stats() const override { return stats_; } - - CSVWriterImpl(io::OutputStream* sink, std::shared_ptr<io::OutputStream> owned_sink, - std::shared_ptr<Schema> schema, - std::vector<std::unique_ptr<ColumnPopulator>> populators, - const WriteOptions& options) - : sink_(sink), - owned_sink_(std::move(owned_sink)), - column_populators_(std::move(populators)), - offsets_(0, 0, ::arrow::stl::allocator<char*>(options.io_context.pool())), - schema_(std::move(schema)), - options_(options) {} - - private: - Status PrepareForContentsWrite() { - // Only called once, as part of initialization - if (data_buffer_ == nullptr) { - ASSIGN_OR_RAISE(data_buffer_, - AllocateResizableBuffer( - options_.batch_size * schema_->num_fields() * kColumnSizeGuess, - options_.io_context.pool())); - } - return Status::OK(); - } - - int64_t CalculateHeaderSize() const { - int64_t header_length = 0; - for (int col = 0; col < schema_->num_fields(); col++) { - const std::string& col_name = schema_->field(col)->name(); - header_length += col_name.size(); - header_length += CountEscapes(col_name); - } - return header_length + (kQuoteDelimiterCount * schema_->num_fields()); - } - - Status WriteHeader() { - // Only called once, as part of initialization - RETURN_NOT_OK(data_buffer_->Resize(CalculateHeaderSize(), /*shrink_to_fit=*/false)); - char* next = - reinterpret_cast<char*>(data_buffer_->mutable_data() + data_buffer_->size() - 1); - for (int col = schema_->num_fields() - 1; col >= 0; col--) { - *next-- = ','; - *next-- = '"'; - next = EscapeReverse(schema_->field(col)->name(), next); - *next-- = '"'; - } - *(data_buffer_->mutable_data() + data_buffer_->size() - 1) = '\n'; - DCHECK_EQ(reinterpret_cast<uint8_t*>(next + 1), data_buffer_->data()); - return sink_->Write(data_buffer_); - } - - Status TranslateMinimalBatch(const RecordBatch& batch) { - if (batch.num_rows() == 0) { - return Status::OK(); - } - offsets_.resize(batch.num_rows()); - std::fill(offsets_.begin(), offsets_.end(), 0); - - // Calculate relative offsets for each row (excluding delimiters) - for (int32_t col = 0; col < static_cast<int32_t>(column_populators_.size()); col++) { - RETURN_NOT_OK( - column_populators_[col]->UpdateRowLengths(*batch.column(col), offsets_.data())); - } - // Calculate cumulalative offsets for each row (including delimiters). - offsets_[0] += batch.num_columns(); - for (int64_t row = 1; row < batch.num_rows(); row++) { - offsets_[row] += offsets_[row - 1] + /*delimiter lengths*/ batch.num_columns(); - } - // Resize the target buffer to required size. We assume batch to batch sizes - // should be pretty close so don't shrink the buffer to avoid allocation churn. - RETURN_NOT_OK(data_buffer_->Resize(offsets_.back(), /*shrink_to_fit=*/false)); - - // Use the offsets to populate contents. - for (auto populator = column_populators_.rbegin(); - populator != column_populators_.rend(); populator++) { - (*populator) - ->PopulateColumns(reinterpret_cast<char*>(data_buffer_->mutable_data()), - offsets_.data()); - } - DCHECK_EQ(0, offsets_[0]); - return Status::OK(); - } - - static constexpr int64_t kColumnSizeGuess = 8; - io::OutputStream* sink_; - std::shared_ptr<io::OutputStream> owned_sink_; - std::vector<std::unique_ptr<ColumnPopulator>> column_populators_; - std::vector<int32_t, arrow::stl::allocator<int32_t>> offsets_; - std::shared_ptr<ResizableBuffer> data_buffer_; - const std::shared_ptr<Schema> schema_; - const WriteOptions options_; - ipc::WriteStats stats_; -}; - -} // namespace - -Status WriteCSV(const Table& table, const WriteOptions& options, - arrow::io::OutputStream* output) { - ASSIGN_OR_RAISE(auto writer, MakeCSVWriter(output, table.schema(), options)); - RETURN_NOT_OK(writer->WriteTable(table)); - return writer->Close(); -} - -Status WriteCSV(const RecordBatch& batch, const WriteOptions& options, - arrow::io::OutputStream* output) { - ASSIGN_OR_RAISE(auto writer, MakeCSVWriter(output, batch.schema(), options)); - RETURN_NOT_OK(writer->WriteRecordBatch(batch)); - return writer->Close(); -} - -ARROW_EXPORT -Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( - std::shared_ptr<io::OutputStream> sink, const std::shared_ptr<Schema>& schema, - const WriteOptions& options) { - return CSVWriterImpl::Make(sink.get(), sink, schema, options); -} - -ARROW_EXPORT -Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( - io::OutputStream* sink, const std::shared_ptr<Schema>& schema, - const WriteOptions& options) { - return CSVWriterImpl::Make(sink, nullptr, schema, options); -} - -} // namespace csv -} // namespace arrow +// 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. + +#include "arrow/csv/writer.h" +#include "arrow/array.h" +#include "arrow/compute/cast.h" +#include "arrow/io/interfaces.h" +#include "arrow/ipc/writer.h" +#include "arrow/record_batch.h" +#include "arrow/result.h" +#include "arrow/result_internal.h" +#include "arrow/stl_allocator.h" +#include "arrow/util/iterator.h" +#include "arrow/util/logging.h" +#include "arrow/util/make_unique.h" + +#include "arrow/visitor_inline.h" + +namespace arrow { +namespace csv { +// This implementation is intentionally light on configurability to minimize the size of +// the initial PR. Aditional features can be added as there is demand and interest to +// implement them. +// +// The algorithm used here at a high level is to break RecordBatches/Tables into slices +// and convert each slice independently. A slice is then converted to CSV by first +// scanning each column to determine the size of its contents when rendered as a string in +// CSV. For non-string types this requires casting the value to string (which is cached). +// This data is used to understand the precise length of each row and a single allocation +// for the final CSV data buffer. Once the final size is known each column is then +// iterated over again to place its contents into the CSV data buffer. The rationale for +// choosing this approach is it allows for reuse of the cast functionality in the compute +// module and inline data visiting functionality in the core library. A performance +// comparison has not been done using a naive single-pass approach. This approach might +// still be competitive due to reduction in the number of per row branches necessary with +// a single pass approach. Profiling would likely yield further opportunities for +// optimization with this approach. + +namespace { + +struct SliceIteratorFunctor { + Result<std::shared_ptr<RecordBatch>> Next() { + if (current_offset < batch->num_rows()) { + std::shared_ptr<RecordBatch> next = batch->Slice(current_offset, slice_size); + current_offset += slice_size; + return next; + } + return IterationTraits<std::shared_ptr<RecordBatch>>::End(); + } + const RecordBatch* const batch; + const int64_t slice_size; + int64_t current_offset; +}; + +RecordBatchIterator RecordBatchSliceIterator(const RecordBatch& batch, + int64_t slice_size) { + SliceIteratorFunctor functor = {&batch, slice_size, /*offset=*/static_cast<int64_t>(0)}; + return RecordBatchIterator(std::move(functor)); +} + +// Counts the number of characters that need escaping in s. +int64_t CountEscapes(util::string_view s) { + return static_cast<int64_t>(std::count(s.begin(), s.end(), '"')); +} + +// Matching quote pair character length. +constexpr int64_t kQuoteCount = 2; +constexpr int64_t kQuoteDelimiterCount = kQuoteCount + /*end_char*/ 1; + +// Interface for generating CSV data per column. +// The intended usage is to iteratively call UpdateRowLengths for a column and +// then PopulateColumns. PopulateColumns must be called in the reverse order of the +// populators (it populates data backwards). +class ColumnPopulator { + public: + ColumnPopulator(MemoryPool* pool, char end_char) : end_char_(end_char), pool_(pool) {} + + virtual ~ColumnPopulator() = default; + + // Adds the number of characters each entry in data will add to to elements + // in row_lengths. + Status UpdateRowLengths(const Array& data, int32_t* row_lengths) { + compute::ExecContext ctx(pool_); + // Populators are intented to be applied to reasonably small data. In most cases + // threading overhead would not be justified. + ctx.set_use_threads(false); + ASSIGN_OR_RAISE( + std::shared_ptr<Array> casted, + compute::Cast(data, /*to_type=*/utf8(), compute::CastOptions(), &ctx)); + casted_array_ = internal::checked_pointer_cast<StringArray>(casted); + return UpdateRowLengths(row_lengths); + } + + // Places string data onto each row in output and updates the corresponding row + // row pointers in preparation for calls to other (preceding) ColumnPopulators. + // Args: + // output: character buffer to write to. + // offsets: an array of end of row column within the the output buffer (values are + // one past the end of the position to write to). + virtual void PopulateColumns(char* output, int32_t* offsets) const = 0; + + protected: + virtual Status UpdateRowLengths(int32_t* row_lengths) = 0; + std::shared_ptr<StringArray> casted_array_; + const char end_char_; + + private: + MemoryPool* const pool_; +}; + +// Copies the contents of to out properly escaping any necessary characters. +// Returns the position prior to last copied character (out_end is decremented). +char* EscapeReverse(arrow::util::string_view s, char* out_end) { + for (const char* val = s.data() + s.length() - 1; val >= s.data(); val--, out_end--) { + if (*val == '"') { + *out_end = *val; + out_end--; + } + *out_end = *val; + } + return out_end; +} + +// Populator for non-string types. This populator relies on compute Cast functionality to +// String if it doesn't exist it will be an error. it also assumes the resulting string +// from a cast does not require quoting or escaping. +class UnquotedColumnPopulator : public ColumnPopulator { + public: + explicit UnquotedColumnPopulator(MemoryPool* memory_pool, char end_char) + : ColumnPopulator(memory_pool, end_char) {} + + Status UpdateRowLengths(int32_t* row_lengths) override { + for (int x = 0; x < casted_array_->length(); x++) { + row_lengths[x] += casted_array_->value_length(x); + } + return Status::OK(); + } + + void PopulateColumns(char* output, int32_t* offsets) const override { + VisitArrayDataInline<StringType>( + *casted_array_->data(), + [&](arrow::util::string_view s) { + int64_t next_column_offset = s.length() + /*end_char*/ 1; + memcpy((output + *offsets - next_column_offset), s.data(), s.length()); + *(output + *offsets - 1) = end_char_; + *offsets -= static_cast<int32_t>(next_column_offset); + offsets++; + }, + [&]() { + // Nulls are empty (unquoted) to distinguish with empty string. + *(output + *offsets - 1) = end_char_; + *offsets -= 1; + offsets++; + }); + } +}; + +// Strings need special handling to ensure they are escaped properly. +// This class handles escaping assuming that all strings will be quoted +// and that the only character within the string that needs to escaped is +// a quote character (") and escaping is done my adding another quote. +class QuotedColumnPopulator : public ColumnPopulator { + public: + QuotedColumnPopulator(MemoryPool* pool, char end_char) + : ColumnPopulator(pool, end_char) {} + + Status UpdateRowLengths(int32_t* row_lengths) override { + const StringArray& input = *casted_array_; + int row_number = 0; + row_needs_escaping_.resize(casted_array_->length()); + VisitArrayDataInline<StringType>( + *input.data(), + [&](arrow::util::string_view s) { + int64_t escaped_count = CountEscapes(s); + // TODO: Maybe use 64 bit row lengths or safe cast? + row_needs_escaping_[row_number] = escaped_count > 0; + row_lengths[row_number] += static_cast<int32_t>(s.length()) + + static_cast<int32_t>(escaped_count + kQuoteCount); + row_number++; + }, + [&]() { + row_needs_escaping_[row_number] = false; + row_number++; + }); + return Status::OK(); + } + + void PopulateColumns(char* output, int32_t* offsets) const override { + auto needs_escaping = row_needs_escaping_.begin(); + VisitArrayDataInline<StringType>( + *(casted_array_->data()), + [&](arrow::util::string_view s) { + // still needs string content length to be added + char* row_end = output + *offsets; + int32_t next_column_offset = 0; + if (!*needs_escaping) { + next_column_offset = static_cast<int32_t>(s.length() + kQuoteDelimiterCount); + memcpy(row_end - next_column_offset + /*quote_offset=*/1, s.data(), + s.length()); + } else { + // Adjust row_end by 3: 1 quote char, 1 end char and 1 to position at the + // first position to write to. + next_column_offset = + static_cast<int32_t>(row_end - EscapeReverse(s, row_end - 3)); + } + *(row_end - next_column_offset) = '"'; + *(row_end - 2) = '"'; + *(row_end - 1) = end_char_; + *offsets -= next_column_offset; + offsets++; + needs_escaping++; + }, + [&]() { + // Nulls are empty (unquoted) to distinguish with empty string. + *(output + *offsets - 1) = end_char_; + *offsets -= 1; + offsets++; + needs_escaping++; + }); + } + + private: + // Older version of GCC don't support custom allocators + // at some point we should change this to use memory_pool + // backed allocator. + std::vector<bool> row_needs_escaping_; +}; + +struct PopulatorFactory { + template <typename TypeClass> + enable_if_t<is_base_binary_type<TypeClass>::value || + std::is_same<FixedSizeBinaryType, TypeClass>::value, + Status> + Visit(const TypeClass& type) { + populator = new QuotedColumnPopulator(pool, end_char); + return Status::OK(); + } + + template <typename TypeClass> + enable_if_dictionary<TypeClass, Status> Visit(const TypeClass& type) { + return VisitTypeInline(*type.value_type(), this); + } + + template <typename TypeClass> + enable_if_t<is_nested_type<TypeClass>::value || is_extension_type<TypeClass>::value, + Status> + Visit(const TypeClass& type) { + return Status::Invalid("Unsupported Type:", type.ToString()); + } + + template <typename TypeClass> + enable_if_t<is_primitive_ctype<TypeClass>::value || is_decimal_type<TypeClass>::value || + is_null_type<TypeClass>::value || is_temporal_type<TypeClass>::value, + Status> + Visit(const TypeClass& type) { + populator = new UnquotedColumnPopulator(pool, end_char); + return Status::OK(); + } + + char end_char; + MemoryPool* pool; + ColumnPopulator* populator; +}; + +Result<std::unique_ptr<ColumnPopulator>> MakePopulator(const Field& field, char end_char, + MemoryPool* pool) { + PopulatorFactory factory{end_char, pool, nullptr}; + RETURN_NOT_OK(VisitTypeInline(*field.type(), &factory)); + return std::unique_ptr<ColumnPopulator>(factory.populator); +} + +class CSVWriterImpl : public ipc::RecordBatchWriter { + public: + static Result<std::shared_ptr<CSVWriterImpl>> Make( + io::OutputStream* sink, std::shared_ptr<io::OutputStream> owned_sink, + std::shared_ptr<Schema> schema, const WriteOptions& options) { + RETURN_NOT_OK(options.Validate()); + std::vector<std::unique_ptr<ColumnPopulator>> populators(schema->num_fields()); + for (int col = 0; col < schema->num_fields(); col++) { + char end_char = col < schema->num_fields() - 1 ? ',' : '\n'; + ASSIGN_OR_RAISE(populators[col], MakePopulator(*schema->field(col), end_char, + options.io_context.pool())); + } + auto writer = std::make_shared<CSVWriterImpl>( + sink, std::move(owned_sink), std::move(schema), std::move(populators), options); + RETURN_NOT_OK(writer->PrepareForContentsWrite()); + if (options.include_header) { + RETURN_NOT_OK(writer->WriteHeader()); + } + return writer; + } + + Status WriteRecordBatch(const RecordBatch& batch) override { + RecordBatchIterator iterator = RecordBatchSliceIterator(batch, options_.batch_size); + for (auto maybe_slice : iterator) { + ASSIGN_OR_RAISE(std::shared_ptr<RecordBatch> slice, maybe_slice); + RETURN_NOT_OK(TranslateMinimalBatch(*slice)); + RETURN_NOT_OK(sink_->Write(data_buffer_)); + stats_.num_record_batches++; + } + return Status::OK(); + } + + Status WriteTable(const Table& table, int64_t max_chunksize) override { + TableBatchReader reader(table); + reader.set_chunksize(max_chunksize > 0 ? max_chunksize : options_.batch_size); + std::shared_ptr<RecordBatch> batch; + RETURN_NOT_OK(reader.ReadNext(&batch)); + while (batch != nullptr) { + RETURN_NOT_OK(TranslateMinimalBatch(*batch)); + RETURN_NOT_OK(sink_->Write(data_buffer_)); + RETURN_NOT_OK(reader.ReadNext(&batch)); + stats_.num_record_batches++; + } + + return Status::OK(); + } + + Status Close() override { return Status::OK(); } + + ipc::WriteStats stats() const override { return stats_; } + + CSVWriterImpl(io::OutputStream* sink, std::shared_ptr<io::OutputStream> owned_sink, + std::shared_ptr<Schema> schema, + std::vector<std::unique_ptr<ColumnPopulator>> populators, + const WriteOptions& options) + : sink_(sink), + owned_sink_(std::move(owned_sink)), + column_populators_(std::move(populators)), + offsets_(0, 0, ::arrow::stl::allocator<char*>(options.io_context.pool())), + schema_(std::move(schema)), + options_(options) {} + + private: + Status PrepareForContentsWrite() { + // Only called once, as part of initialization + if (data_buffer_ == nullptr) { + ASSIGN_OR_RAISE(data_buffer_, + AllocateResizableBuffer( + options_.batch_size * schema_->num_fields() * kColumnSizeGuess, + options_.io_context.pool())); + } + return Status::OK(); + } + + int64_t CalculateHeaderSize() const { + int64_t header_length = 0; + for (int col = 0; col < schema_->num_fields(); col++) { + const std::string& col_name = schema_->field(col)->name(); + header_length += col_name.size(); + header_length += CountEscapes(col_name); + } + return header_length + (kQuoteDelimiterCount * schema_->num_fields()); + } + + Status WriteHeader() { + // Only called once, as part of initialization + RETURN_NOT_OK(data_buffer_->Resize(CalculateHeaderSize(), /*shrink_to_fit=*/false)); + char* next = + reinterpret_cast<char*>(data_buffer_->mutable_data() + data_buffer_->size() - 1); + for (int col = schema_->num_fields() - 1; col >= 0; col--) { + *next-- = ','; + *next-- = '"'; + next = EscapeReverse(schema_->field(col)->name(), next); + *next-- = '"'; + } + *(data_buffer_->mutable_data() + data_buffer_->size() - 1) = '\n'; + DCHECK_EQ(reinterpret_cast<uint8_t*>(next + 1), data_buffer_->data()); + return sink_->Write(data_buffer_); + } + + Status TranslateMinimalBatch(const RecordBatch& batch) { + if (batch.num_rows() == 0) { + return Status::OK(); + } + offsets_.resize(batch.num_rows()); + std::fill(offsets_.begin(), offsets_.end(), 0); + + // Calculate relative offsets for each row (excluding delimiters) + for (int32_t col = 0; col < static_cast<int32_t>(column_populators_.size()); col++) { + RETURN_NOT_OK( + column_populators_[col]->UpdateRowLengths(*batch.column(col), offsets_.data())); + } + // Calculate cumulalative offsets for each row (including delimiters). + offsets_[0] += batch.num_columns(); + for (int64_t row = 1; row < batch.num_rows(); row++) { + offsets_[row] += offsets_[row - 1] + /*delimiter lengths*/ batch.num_columns(); + } + // Resize the target buffer to required size. We assume batch to batch sizes + // should be pretty close so don't shrink the buffer to avoid allocation churn. + RETURN_NOT_OK(data_buffer_->Resize(offsets_.back(), /*shrink_to_fit=*/false)); + + // Use the offsets to populate contents. + for (auto populator = column_populators_.rbegin(); + populator != column_populators_.rend(); populator++) { + (*populator) + ->PopulateColumns(reinterpret_cast<char*>(data_buffer_->mutable_data()), + offsets_.data()); + } + DCHECK_EQ(0, offsets_[0]); + return Status::OK(); + } + + static constexpr int64_t kColumnSizeGuess = 8; + io::OutputStream* sink_; + std::shared_ptr<io::OutputStream> owned_sink_; + std::vector<std::unique_ptr<ColumnPopulator>> column_populators_; + std::vector<int32_t, arrow::stl::allocator<int32_t>> offsets_; + std::shared_ptr<ResizableBuffer> data_buffer_; + const std::shared_ptr<Schema> schema_; + const WriteOptions options_; + ipc::WriteStats stats_; +}; + +} // namespace + +Status WriteCSV(const Table& table, const WriteOptions& options, + arrow::io::OutputStream* output) { + ASSIGN_OR_RAISE(auto writer, MakeCSVWriter(output, table.schema(), options)); + RETURN_NOT_OK(writer->WriteTable(table)); + return writer->Close(); +} + +Status WriteCSV(const RecordBatch& batch, const WriteOptions& options, + arrow::io::OutputStream* output) { + ASSIGN_OR_RAISE(auto writer, MakeCSVWriter(output, batch.schema(), options)); + RETURN_NOT_OK(writer->WriteRecordBatch(batch)); + return writer->Close(); +} + +ARROW_EXPORT +Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( + std::shared_ptr<io::OutputStream> sink, const std::shared_ptr<Schema>& schema, + const WriteOptions& options) { + return CSVWriterImpl::Make(sink.get(), sink, schema, options); +} + +ARROW_EXPORT +Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( + io::OutputStream* sink, const std::shared_ptr<Schema>& schema, + const WriteOptions& options) { + return CSVWriterImpl::Make(sink, nullptr, schema, options); +} + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.h b/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.h index bb31b223a8..2f1442ae0a 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/csv/writer.h @@ -1,73 +1,73 @@ -// 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 <memory> - -#include "arrow/csv/options.h" -#include "arrow/io/interfaces.h" -#include "arrow/ipc/type_fwd.h" -#include "arrow/record_batch.h" -#include "arrow/table.h" - -namespace arrow { -namespace csv { -// Functionality for converting Arrow data to Comma separated value text. -// This library supports all primitive types that can be cast to a StringArrays. -// It applies to following formatting rules: -// - For non-binary types no quotes surround values. Nulls are represented as the empty -// string. -// - For binary types all non-null data is quoted (and quotes within data are escaped -// with an additional quote). -// Null values are empty and unquoted. -// - LF (\n) is always used as a line ending. - -/// \brief Converts table to a CSV and writes the results to output. -/// Experimental -ARROW_EXPORT Status WriteCSV(const Table& table, const WriteOptions& options, - arrow::io::OutputStream* output); -/// \brief Converts batch to CSV and writes the results to output. -/// Experimental -ARROW_EXPORT Status WriteCSV(const RecordBatch& batch, const WriteOptions& options, - arrow::io::OutputStream* output); - -/// \brief Create a new CSV writer. User is responsible for closing the -/// actual OutputStream. -/// -/// \param[in] sink output stream to write to -/// \param[in] schema the schema of the record batches to be written -/// \param[in] options options for serialization -/// \return Result<std::shared_ptr<RecordBatchWriter>> -ARROW_EXPORT -Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( - std::shared_ptr<io::OutputStream> sink, const std::shared_ptr<Schema>& schema, - const WriteOptions& options = WriteOptions::Defaults()); - -/// \brief Create a new CSV writer. -/// -/// \param[in] sink output stream to write to (does not take ownership) -/// \param[in] schema the schema of the record batches to be written -/// \param[in] options options for serialization -/// \return Result<std::shared_ptr<RecordBatchWriter>> -ARROW_EXPORT -Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( - io::OutputStream* sink, const std::shared_ptr<Schema>& schema, - const WriteOptions& options = WriteOptions::Defaults()); - -} // namespace csv -} // namespace arrow +// 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 <memory> + +#include "arrow/csv/options.h" +#include "arrow/io/interfaces.h" +#include "arrow/ipc/type_fwd.h" +#include "arrow/record_batch.h" +#include "arrow/table.h" + +namespace arrow { +namespace csv { +// Functionality for converting Arrow data to Comma separated value text. +// This library supports all primitive types that can be cast to a StringArrays. +// It applies to following formatting rules: +// - For non-binary types no quotes surround values. Nulls are represented as the empty +// string. +// - For binary types all non-null data is quoted (and quotes within data are escaped +// with an additional quote). +// Null values are empty and unquoted. +// - LF (\n) is always used as a line ending. + +/// \brief Converts table to a CSV and writes the results to output. +/// Experimental +ARROW_EXPORT Status WriteCSV(const Table& table, const WriteOptions& options, + arrow::io::OutputStream* output); +/// \brief Converts batch to CSV and writes the results to output. +/// Experimental +ARROW_EXPORT Status WriteCSV(const RecordBatch& batch, const WriteOptions& options, + arrow::io::OutputStream* output); + +/// \brief Create a new CSV writer. User is responsible for closing the +/// actual OutputStream. +/// +/// \param[in] sink output stream to write to +/// \param[in] schema the schema of the record batches to be written +/// \param[in] options options for serialization +/// \return Result<std::shared_ptr<RecordBatchWriter>> +ARROW_EXPORT +Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( + std::shared_ptr<io::OutputStream> sink, const std::shared_ptr<Schema>& schema, + const WriteOptions& options = WriteOptions::Defaults()); + +/// \brief Create a new CSV writer. +/// +/// \param[in] sink output stream to write to (does not take ownership) +/// \param[in] schema the schema of the record batches to be written +/// \param[in] options options for serialization +/// \return Result<std::shared_ptr<RecordBatchWriter>> +ARROW_EXPORT +Result<std::shared_ptr<ipc::RecordBatchWriter>> MakeCSVWriter( + io::OutputStream* sink, const std::shared_ptr<Schema>& schema, + const WriteOptions& options = WriteOptions::Defaults()); + +} // namespace csv +} // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/util/int128_internal.h b/contrib/libs/apache/arrow/cpp/src/arrow/util/int128_internal.h index b7d40118b4..1d494671a9 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/util/int128_internal.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/util/int128_internal.h @@ -20,7 +20,7 @@ #include "arrow/util/macros.h" #ifndef ARROW_USE_NATIVE_INT128 -#include <boost/multiprecision/cpp_int.hpp> +#include <boost/multiprecision/cpp_int.hpp> #endif namespace arrow { diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/util/logging.cc b/contrib/libs/apache/arrow/cpp/src/arrow/util/logging.cc index f212d3d406..65359b4408 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/util/logging.cc +++ b/contrib/libs/apache/arrow/cpp/src/arrow/util/logging.cc @@ -28,7 +28,7 @@ #include <signal.h> #include <vector> -#error #include "glog/logging.h" +#error #include "glog/logging.h" // Restore our versions of DCHECK and friends, as GLog defines its own #undef DCHECK diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/util/optional.h b/contrib/libs/apache/arrow/cpp/src/arrow/util/optional.h index 546c74bb32..b824b499bb 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/util/optional.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/util/optional.h @@ -17,17 +17,17 @@ #pragma once -#include <optional> +#include <optional> namespace arrow { namespace util { template <typename T> -using optional = std::optional<T>; +using optional = std::optional<T>; -using std::bad_optional_access; -using std::make_optional; -using std::nullopt; +using std::bad_optional_access; +using std::make_optional; +using std::nullopt; } // namespace util } // namespace arrow diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.h b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.h index f3edfcd1dc..46567d69b1 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.h +++ b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.h @@ -1,53 +1,53 @@ -// -// ios.h -// DateTimeLib -// -// The MIT License (MIT) -// -// Copyright (c) 2016 Alexander Kormanovsky -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef ios_hpp -#define ios_hpp - -#if __APPLE__ -# include <TargetConditionals.h> -# if TARGET_OS_IPHONE -# include <string> - - namespace arrow_vendored - { - namespace date - { - namespace iOSUtils - { - - std::string get_tzdata_path(); - std::string get_current_timezone(); - - } // namespace iOSUtils - } // namespace date - } // namespace arrow_vendored - -# endif // TARGET_OS_IPHONE -#else // !__APPLE__ -# define TARGET_OS_IPHONE 0 -#endif // !__APPLE__ -#endif // ios_hpp +// +// ios.h +// DateTimeLib +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Alexander Kormanovsky +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ios_hpp +#define ios_hpp + +#if __APPLE__ +# include <TargetConditionals.h> +# if TARGET_OS_IPHONE +# include <string> + + namespace arrow_vendored + { + namespace date + { + namespace iOSUtils + { + + std::string get_tzdata_path(); + std::string get_current_timezone(); + + } // namespace iOSUtils + } // namespace date + } // namespace arrow_vendored + +# endif // TARGET_OS_IPHONE +#else // !__APPLE__ +# define TARGET_OS_IPHONE 0 +#endif // !__APPLE__ +#endif // ios_hpp diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.mm b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.mm index 7d432afe85..18c521201d 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.mm +++ b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/ios.mm @@ -1,340 +1,340 @@ -// -// The MIT License (MIT) -// -// Copyright (c) 2016 Alexander Kormanovsky -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -#include "ios.h" - -#if TARGET_OS_IPHONE - -#include <Foundation/Foundation.h> - -#include <fstream> -#include <zlib.h> -#include <sys/stat.h> - -#ifndef TAR_DEBUG -# define TAR_DEBUG 0 -#endif - -#define INTERNAL_DIR "Library" -#define TZDATA_DIR "tzdata" -#define TARGZ_EXTENSION "tar.gz" - -#define TAR_BLOCK_SIZE 512 -#define TAR_TYPE_POSITION 156 -#define TAR_NAME_POSITION 0 -#define TAR_NAME_SIZE 100 -#define TAR_SIZE_POSITION 124 -#define TAR_SIZE_SIZE 12 - -namespace arrow_vendored -{ -namespace date -{ - namespace iOSUtils - { - - struct TarInfo - { - char objType; - std::string objName; - size_t realContentSize; // writable size without padding zeroes - size_t blocksContentSize; // adjusted size to 512 bytes blocks - bool success; - }; - - std::string convertCFStringRefPathToCStringPath(CFStringRef ref); - bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); - TarInfo getTarObjectInfo(std::ifstream &readStream); - std::string getTarObject(std::ifstream &readStream, int64_t size); - bool writeFile(const std::string &tzdataPath, const std::string &fileName, - const std::string &data, size_t realContentSize); - - std::string - get_current_timezone() - { - CFTimeZoneRef tzRef = CFTimeZoneCopySystem(); - CFStringRef tzNameRef = CFTimeZoneGetName(tzRef); - CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1; - char buffer[bufferSize]; - - if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8)) - { - CFRelease(tzRef); - return std::string(buffer); - } - - CFRelease(tzRef); - - return ""; - } - - std::string - get_tzdata_path() - { - CFURLRef homeUrlRef = CFCopyHomeDirectoryURL(); - CFStringRef homePath = CFURLCopyPath(homeUrlRef); - std::string path(std::string(convertCFStringRefPathToCStringPath(homePath)) + - INTERNAL_DIR + "/" + TZDATA_DIR); - std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + - INTERNAL_DIR); - - if (access(path.c_str(), F_OK) == 0) - { -#if TAR_DEBUG - printf("tzdata dir exists\n"); -#endif - CFRelease(homeUrlRef); - CFRelease(homePath); - - return result_path; - } - - CFBundleRef mainBundle = CFBundleGetMainBundle(); - CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), - NULL); - - if (CFArrayGetCount(paths) != 0) - { - // get archive path, assume there is no other tar.gz in bundle - CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0)); - CFStringRef archiveName = CFURLCopyPath(archiveUrl); - archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); - - extractTzdata(homeUrlRef, archiveUrl, path); - - CFRelease(archiveUrl); - CFRelease(archiveName); - } - - CFRelease(homeUrlRef); - CFRelease(homePath); - CFRelease(paths); - - return result_path; - } - - std::string - convertCFStringRefPathToCStringPath(CFStringRef ref) - { - CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); - char *buffer = new char[bufferSize]; - CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); - auto result = std::string(buffer); - delete[] buffer; - return result; - } - - bool - extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) - { - std::string TAR_TMP_PATH = "/tmp.tar"; - - CFStringRef homeStringRef = CFURLCopyPath(homeUrl); - auto homePath = convertCFStringRefPathToCStringPath(homeStringRef); - CFRelease(homeStringRef); - - CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl); - auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef); - CFRelease(archiveStringRef); - - // create Library path - auto libraryPath = homePath + INTERNAL_DIR; - - // create tzdata path - auto tzdataPath = libraryPath + "/" + TZDATA_DIR; - - // -- replace %20 with " " - const std::string search = "%20"; - const std::string replacement = " "; - size_t pos = 0; - - while ((pos = archivePath.find(search, pos)) != std::string::npos) { - archivePath.replace(pos, search.length(), replacement); - pos += replacement.length(); - } - - gzFile tarFile = gzopen(archivePath.c_str(), "rb"); - - // create tar unpacking path - auto tarPath = libraryPath + TAR_TMP_PATH; - - // create tzdata directory - mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - // ======= extract tar ======== - - std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app); - unsigned int bufferLength = 1024 * 256; // 256Kb - unsigned char *buffer = (unsigned char *)malloc(bufferLength); - bool success = true; - - while (true) - { - int readBytes = gzread(tarFile, buffer, bufferLength); - - if (readBytes > 0) - { - os.write((char *) &buffer[0], readBytes); - } - else - if (readBytes == 0) - { - break; - } - else - if (readBytes == -1) - { - printf("decompression failed\n"); - success = false; - break; - } - else - { - printf("unexpected zlib state\n"); - success = false; - break; - } - } - - os.close(); - free(buffer); - gzclose(tarFile); - - if (!success) - { - remove(tarPath.c_str()); - return false; - } - - // ======== extract files ========= - - uint64_t location = 0; // Position in the file - - // get file size - struct stat stat_buf; - int res = stat(tarPath.c_str(), &stat_buf); - if (res != 0) - { - printf("error file size\n"); - remove(tarPath.c_str()); - return false; - } - int64_t tarSize = stat_buf.st_size; - - // create read stream - std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary); - - // process files - while (location < tarSize) - { - TarInfo info = getTarObjectInfo(is); - - if (!info.success || info.realContentSize == 0) - { - break; // something wrong or all files are read - } - - switch (info.objType) - { - case '0': // file - case '\0': // - { - std::string obj = getTarObject(is, info.blocksContentSize); -#if TAR_DEBUG - size += info.realContentSize; - printf("#%i %s file size %lld written total %ld from %lld\n", ++count, - info.objName.c_str(), info.realContentSize, size, tarSize); -#endif - writeFile(tzdataPath, info.objName, obj, info.realContentSize); - location += info.blocksContentSize; - - break; - } - } - } - - remove(tarPath.c_str()); - - return true; - } - - TarInfo - getTarObjectInfo(std::ifstream &readStream) - { - int64_t length = TAR_BLOCK_SIZE; - char buffer[length]; - char type; - char name[TAR_NAME_SIZE + 1]; - char sizeBuf[TAR_SIZE_SIZE + 1]; - - readStream.read(buffer, length); - - memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); - - memset(&name, '\0', TAR_NAME_SIZE + 1); - memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); - - memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); - memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); - size_t realSize = strtol(sizeBuf, NULL, 8); - size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); - - return {type, std::string(name), realSize, blocksSize, true}; - } - - std::string - getTarObject(std::ifstream &readStream, int64_t size) - { - char buffer[size]; - readStream.read(buffer, size); - return std::string(buffer); - } - - bool - writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data, - size_t realContentSize) - { - std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary); - - if (!os) { - return false; - } - - // trim empty space - char trimmedData[realContentSize + 1]; - memset(&trimmedData, '\0', realContentSize); - memcpy(&trimmedData, data.c_str(), realContentSize); - - // write - os.write(trimmedData, realContentSize); - os.close(); - - return true; - } - - } // namespace iOSUtils -} // namespace date -} // namespace arrow_vendored - -#endif // TARGET_OS_IPHONE +// +// The MIT License (MIT) +// +// Copyright (c) 2016 Alexander Kormanovsky +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#include "ios.h" + +#if TARGET_OS_IPHONE + +#include <Foundation/Foundation.h> + +#include <fstream> +#include <zlib.h> +#include <sys/stat.h> + +#ifndef TAR_DEBUG +# define TAR_DEBUG 0 +#endif + +#define INTERNAL_DIR "Library" +#define TZDATA_DIR "tzdata" +#define TARGZ_EXTENSION "tar.gz" + +#define TAR_BLOCK_SIZE 512 +#define TAR_TYPE_POSITION 156 +#define TAR_NAME_POSITION 0 +#define TAR_NAME_SIZE 100 +#define TAR_SIZE_POSITION 124 +#define TAR_SIZE_SIZE 12 + +namespace arrow_vendored +{ +namespace date +{ + namespace iOSUtils + { + + struct TarInfo + { + char objType; + std::string objName; + size_t realContentSize; // writable size without padding zeroes + size_t blocksContentSize; // adjusted size to 512 bytes blocks + bool success; + }; + + std::string convertCFStringRefPathToCStringPath(CFStringRef ref); + bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); + TarInfo getTarObjectInfo(std::ifstream &readStream); + std::string getTarObject(std::ifstream &readStream, int64_t size); + bool writeFile(const std::string &tzdataPath, const std::string &fileName, + const std::string &data, size_t realContentSize); + + std::string + get_current_timezone() + { + CFTimeZoneRef tzRef = CFTimeZoneCopySystem(); + CFStringRef tzNameRef = CFTimeZoneGetName(tzRef); + CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1; + char buffer[bufferSize]; + + if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8)) + { + CFRelease(tzRef); + return std::string(buffer); + } + + CFRelease(tzRef); + + return ""; + } + + std::string + get_tzdata_path() + { + CFURLRef homeUrlRef = CFCopyHomeDirectoryURL(); + CFStringRef homePath = CFURLCopyPath(homeUrlRef); + std::string path(std::string(convertCFStringRefPathToCStringPath(homePath)) + + INTERNAL_DIR + "/" + TZDATA_DIR); + std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + + INTERNAL_DIR); + + if (access(path.c_str(), F_OK) == 0) + { +#if TAR_DEBUG + printf("tzdata dir exists\n"); +#endif + CFRelease(homeUrlRef); + CFRelease(homePath); + + return result_path; + } + + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), + NULL); + + if (CFArrayGetCount(paths) != 0) + { + // get archive path, assume there is no other tar.gz in bundle + CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0)); + CFStringRef archiveName = CFURLCopyPath(archiveUrl); + archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); + + extractTzdata(homeUrlRef, archiveUrl, path); + + CFRelease(archiveUrl); + CFRelease(archiveName); + } + + CFRelease(homeUrlRef); + CFRelease(homePath); + CFRelease(paths); + + return result_path; + } + + std::string + convertCFStringRefPathToCStringPath(CFStringRef ref) + { + CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); + char *buffer = new char[bufferSize]; + CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); + auto result = std::string(buffer); + delete[] buffer; + return result; + } + + bool + extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) + { + std::string TAR_TMP_PATH = "/tmp.tar"; + + CFStringRef homeStringRef = CFURLCopyPath(homeUrl); + auto homePath = convertCFStringRefPathToCStringPath(homeStringRef); + CFRelease(homeStringRef); + + CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl); + auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef); + CFRelease(archiveStringRef); + + // create Library path + auto libraryPath = homePath + INTERNAL_DIR; + + // create tzdata path + auto tzdataPath = libraryPath + "/" + TZDATA_DIR; + + // -- replace %20 with " " + const std::string search = "%20"; + const std::string replacement = " "; + size_t pos = 0; + + while ((pos = archivePath.find(search, pos)) != std::string::npos) { + archivePath.replace(pos, search.length(), replacement); + pos += replacement.length(); + } + + gzFile tarFile = gzopen(archivePath.c_str(), "rb"); + + // create tar unpacking path + auto tarPath = libraryPath + TAR_TMP_PATH; + + // create tzdata directory + mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + // ======= extract tar ======== + + std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app); + unsigned int bufferLength = 1024 * 256; // 256Kb + unsigned char *buffer = (unsigned char *)malloc(bufferLength); + bool success = true; + + while (true) + { + int readBytes = gzread(tarFile, buffer, bufferLength); + + if (readBytes > 0) + { + os.write((char *) &buffer[0], readBytes); + } + else + if (readBytes == 0) + { + break; + } + else + if (readBytes == -1) + { + printf("decompression failed\n"); + success = false; + break; + } + else + { + printf("unexpected zlib state\n"); + success = false; + break; + } + } + + os.close(); + free(buffer); + gzclose(tarFile); + + if (!success) + { + remove(tarPath.c_str()); + return false; + } + + // ======== extract files ========= + + uint64_t location = 0; // Position in the file + + // get file size + struct stat stat_buf; + int res = stat(tarPath.c_str(), &stat_buf); + if (res != 0) + { + printf("error file size\n"); + remove(tarPath.c_str()); + return false; + } + int64_t tarSize = stat_buf.st_size; + + // create read stream + std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary); + + // process files + while (location < tarSize) + { + TarInfo info = getTarObjectInfo(is); + + if (!info.success || info.realContentSize == 0) + { + break; // something wrong or all files are read + } + + switch (info.objType) + { + case '0': // file + case '\0': // + { + std::string obj = getTarObject(is, info.blocksContentSize); +#if TAR_DEBUG + size += info.realContentSize; + printf("#%i %s file size %lld written total %ld from %lld\n", ++count, + info.objName.c_str(), info.realContentSize, size, tarSize); +#endif + writeFile(tzdataPath, info.objName, obj, info.realContentSize); + location += info.blocksContentSize; + + break; + } + } + } + + remove(tarPath.c_str()); + + return true; + } + + TarInfo + getTarObjectInfo(std::ifstream &readStream) + { + int64_t length = TAR_BLOCK_SIZE; + char buffer[length]; + char type; + char name[TAR_NAME_SIZE + 1]; + char sizeBuf[TAR_SIZE_SIZE + 1]; + + readStream.read(buffer, length); + + memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); + + memset(&name, '\0', TAR_NAME_SIZE + 1); + memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); + + memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); + memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); + size_t realSize = strtol(sizeBuf, NULL, 8); + size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); + + return {type, std::string(name), realSize, blocksSize, true}; + } + + std::string + getTarObject(std::ifstream &readStream, int64_t size) + { + char buffer[size]; + readStream.read(buffer, size); + return std::string(buffer); + } + + bool + writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data, + size_t realContentSize) + { + std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary); + + if (!os) { + return false; + } + + // trim empty space + char trimmedData[realContentSize + 1]; + memset(&trimmedData, '\0', realContentSize); + memcpy(&trimmedData, data.c_str(), realContentSize); + + // write + os.write(trimmedData, realContentSize); + os.close(); + + return true; + } + + } // namespace iOSUtils +} // namespace date +} // namespace arrow_vendored + +#endif // TARGET_OS_IPHONE diff --git a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/tz.cpp b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/tz.cpp index b1ae8a70fe..e80e392bd7 100644 --- a/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/tz.cpp +++ b/contrib/libs/apache/arrow/cpp/src/arrow/vendored/datetime/tz.cpp @@ -89,7 +89,7 @@ #include "tz_private.h" #ifdef __APPLE__ -# include "ios.h" +# include "ios.h" #else # define TARGET_OS_IPHONE 0 # define TARGET_OS_SIMULATOR 0 @@ -1309,7 +1309,7 @@ void detail::Rule::split(std::vector<Rule>& rules, std::size_t i, std::size_t k, std::size_t& e) { using namespace date; - using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; + using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; // rules[i].starting_year_ <= rules[k].starting_year_ && // rules[i].ending_year_ >= rules[k].starting_year_ && // (rules[i].starting_year_ != rules[k].starting_year_ || @@ -1377,7 +1377,7 @@ detail::Rule::split(std::vector<Rule>& rules, std::size_t i, std::size_t k, std: void detail::Rule::split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e) { - using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; + using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; auto j = i; for (; i + 1 < e; ++i) { @@ -1401,7 +1401,7 @@ detail::Rule::split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_ void detail::Rule::split_overlaps(std::vector<Rule>& rules) { - using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; + using difference_type = std::iterator_traits<std::vector<Rule>::iterator>::difference_type; for (std::size_t i = 0; i < rules.size();) { auto e = static_cast<std::size_t>(std::upper_bound( diff --git a/contrib/libs/apache/arrow/src/arrow/util/config.h b/contrib/libs/apache/arrow/src/arrow/util/config.h index 49dfcf79a3..2d46017e47 100644 --- a/contrib/libs/apache/arrow/src/arrow/util/config.h +++ b/contrib/libs/apache/arrow/src/arrow/util/config.h @@ -35,7 +35,7 @@ #define ARROW_PACKAGE_KIND "" #define ARROW_COMPUTE -#define ARROW_CSV +#define ARROW_CSV /* #undef ARROW_DATASET */ /* #undef ARROW_FILESYSTEM */ /* #undef ARROW_FLIGHT */ diff --git a/contrib/libs/apache/arrow/ya.make b/contrib/libs/apache/arrow/ya.make index e85b49d3f6..27b9235d9e 100644 --- a/contrib/libs/apache/arrow/ya.make +++ b/contrib/libs/apache/arrow/ya.make @@ -1,4 +1,4 @@ -# Generated by devtools/yamaker from nixpkgs 3322db8e36d0b32700737d8de7315bd9e9c2b21a. +# Generated by devtools/yamaker from nixpkgs 3322db8e36d0b32700737d8de7315bd9e9c2b21a. LIBRARY() @@ -35,7 +35,7 @@ PEERDIR( contrib/libs/lz4 contrib/libs/re2 contrib/libs/snappy - contrib/libs/utf8proc + contrib/libs/utf8proc contrib/libs/xxhash contrib/libs/zlib contrib/libs/zstd @@ -53,9 +53,9 @@ ADDINCL( contrib/libs/flatbuffers/include contrib/libs/lz4 contrib/libs/re2 - contrib/libs/utf8proc + contrib/libs/utf8proc contrib/libs/zstd/include - contrib/restricted/boost + contrib/restricted/boost ) NO_COMPILER_WARNINGS() @@ -63,14 +63,14 @@ NO_COMPILER_WARNINGS() NO_UTIL() CFLAGS( - GLOBAL -DARROW_STATIC + GLOBAL -DARROW_STATIC -DARROW_EXPORTING -DARROW_WITH_BROTLI -DARROW_WITH_LZ4 -DARROW_WITH_RE2 -DARROW_WITH_SNAPPY -DARROW_WITH_TIMING_TESTS - -DARROW_WITH_UTF8PROC + -DARROW_WITH_UTF8PROC -DARROW_WITH_ZLIB -DARROW_WITH_ZSTD -DHAVE_INTTYPES_H @@ -80,9 +80,9 @@ CFLAGS( ) IF (NOT OS_WINDOWS) - CFLAGS( - -DHAVE_NETINET_IN_H - ) + CFLAGS( + -DHAVE_NETINET_IN_H + ) ENDIF() SRCS( @@ -159,14 +159,14 @@ SRCS( cpp/src/arrow/compute/kernels/vector_sort.cc cpp/src/arrow/compute/registry.cc cpp/src/arrow/config.cc - cpp/src/arrow/csv/chunker.cc - cpp/src/arrow/csv/column_builder.cc - cpp/src/arrow/csv/column_decoder.cc - cpp/src/arrow/csv/converter.cc - cpp/src/arrow/csv/options.cc - cpp/src/arrow/csv/parser.cc - cpp/src/arrow/csv/reader.cc - cpp/src/arrow/csv/writer.cc + cpp/src/arrow/csv/chunker.cc + cpp/src/arrow/csv/column_builder.cc + cpp/src/arrow/csv/column_decoder.cc + cpp/src/arrow/csv/converter.cc + cpp/src/arrow/csv/options.cc + cpp/src/arrow/csv/parser.cc + cpp/src/arrow/csv/reader.cc + cpp/src/arrow/csv/writer.cc cpp/src/arrow/datum.cc cpp/src/arrow/device.cc cpp/src/arrow/extension_type.cc |