diff --git a/cpp/src/parquet/statistics.cc b/cpp/src/parquet/statistics.cc index 09c48ef0ff69..351601972c68 100644 --- a/cpp/src/parquet/statistics.cc +++ b/cpp/src/parquet/statistics.cc @@ -29,6 +29,7 @@ #include "arrow/type_traits.h" #include "arrow/util/bit_run_reader.h" #include "arrow/util/checked_cast.h" +#include "arrow/util/endian.h" #include "arrow/util/float16.h" #include "arrow/util/logging_internal.h" #include "arrow/util/ubsan.h" @@ -923,19 +924,95 @@ void TypedStatisticsImpl::UpdateSpaced(const T* values, const uint8_t* va valid_bits_offset)); } +template +T ToLittleEndianValue(const T& value) { +#if ARROW_LITTLE_ENDIAN + return value; +#else + if constexpr (std::is_integral_v) { + return ::arrow::bit_util::ToLittleEndian(value); + } else if constexpr (std::is_floating_point_v) { + using UInt = std::conditional_t>; + + UInt bits; + std::memcpy(&bits, &value, sizeof(bits)); + bits = ::arrow::bit_util::ToLittleEndian(bits); + + T out; + std::memcpy(&out, &bits, sizeof(out)); + return out; + } else { + return value; // non-numeric types handled elsewhere + } +#endif +} + +template +T FromLittleEndianValue(const char* src, size_t src_size) { +#if ARROW_LITTLE_ENDIAN + T out{}; + std::memcpy(&out, src, std::min(src_size, sizeof(T))); + return out; +#else + if constexpr (std::is_integral_v) { + T value{}; + std::memcpy(&value, src, std::min(src_size, sizeof(T))); + return ::arrow::bit_util::FromLittleEndian(value); + } else if constexpr (std::is_floating_point_v) { + using UInt = std::conditional_t>; + + UInt bits{}; + std::memcpy(&bits, src, std::min(src_size, sizeof(bits))); + bits = ::arrow::bit_util::FromLittleEndian(bits); + + T out; + std::memcpy(&out, &bits, sizeof(out)); + return out; + } else { + T out{}; + std::memcpy(&out, src, std::min(src_size, sizeof(T))); + return out; + } +#endif +} + +template +constexpr bool kIsArithmeticType = std::is_arithmetic_v; + template void TypedStatisticsImpl::PlainEncode(const T& src, std::string* dst) const { + using CType = typename DType::c_type; + + // Fast path: fixed-width arithmetic types (int32/int64/float/double) + if constexpr (kIsArithmeticType) { + CType le_value = ToLittleEndianValue(src); + dst->assign(reinterpret_cast(&le_value), sizeof(le_value)); + return; + } + + // Generic fallback for non-arithmetic types auto encoder = MakeTypedEncoder(Encoding::PLAIN, false, descr_, pool_); encoder->Put(&src, 1); auto buffer = encoder->FlushValues(); - auto ptr = reinterpret_cast(buffer->data()); - dst->assign(ptr, static_cast(buffer->size())); + dst->assign(reinterpret_cast(buffer->data()), + static_cast(buffer->size())); } template void TypedStatisticsImpl::PlainDecode(const std::string& src, T* dst) const { + using CType = typename DType::c_type; + + // Fast path: fixed-width arithmetic types + if constexpr (kIsArithmeticType) { + *dst = FromLittleEndianValue(src.data(), src.size()); + return; + } + + // Fallback for non-arithmetic types auto decoder = MakeTypedDecoder(Encoding::PLAIN, descr_); - decoder->SetData(1, reinterpret_cast(src.c_str()), + decoder->SetData(1, reinterpret_cast(src.data()), static_cast(src.size())); int decoded_values = decoder->Decode(dst, 1); if (decoded_values != 1) {