| /* | |
| * 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 "paimon/data/decimal.h" | |
| #include <algorithm> | |
| #include <cmath> | |
| #include <cstdlib> | |
| #include "arrow/api.h" | |
| #include "arrow/scalar.h" | |
| #include "arrow/util/decimal.h" | |
| #include "paimon/io/byte_order.h" | |
| #include "paimon/memory/bytes.h" | |
| namespace paimon { | |
| const int64_t Decimal::POWERS_OF_TEN[MAX_COMPACT_PRECISION + 1] = {1, | |
| 10, | |
| 100, | |
| 1000, | |
| 10000, | |
| 100000, | |
| 1000000l, | |
| 10000000l, | |
| 100000000l, | |
| 1000000000l, | |
| 10000000000l, | |
| 100000000000l, | |
| 1000000000000l, | |
| 10000000000000l, | |
| 100000000000000l, | |
| 1000000000000000l, | |
| 10000000000000000l, | |
| 100000000000000000l, | |
| 1000000000000000000l}; | |
| const Decimal::int128_t Decimal::INT128_MAXIMUM_VALUE = static_cast<Decimal::int128_t>( | |
| static_cast<Decimal::uint128_t>(0x7fffffffffffffffULL) << 64 | 0xffffffffffffffff); | |
| const Decimal::int128_t Decimal::INT128_MINIMUM_VALUE = | |
| static_cast<Decimal::int128_t>(static_cast<Decimal::uint128_t>(0x8000000000000000ULL) << 64); | |
| std::string Decimal::ToString() const { | |
| auto type = arrow::decimal128(Precision(), Scale()); | |
| arrow::Decimal128Scalar scalar(arrow::Decimal128(HighBits(), LowBits()), type); | |
| return scalar.ToString(); | |
| } | |
| std::vector<char> Decimal::ToUnscaledBytes() const { | |
| bool positive = value_ >= 0; | |
| int32_t valid_bytes = 0; | |
| if (positive) { | |
| int32_t leading_zero_bytes = count_leading_zero_bytes(value_); | |
| valid_bytes = sizeof(value_) - leading_zero_bytes; | |
| } else { | |
| int32_t leading_all_ones_bytes = count_leading_all_ones_bytes(value_); | |
| valid_bytes = sizeof(value_) - leading_all_ones_bytes; | |
| } | |
| if (valid_bytes == 0) { | |
| // if value_ == 0, return one byte with 0; | |
| // if value_ == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, return one byte with 0xFF | |
| valid_bytes = 1; | |
| } else { | |
| // java BigInteger use highest significant bit to determine if the number is positive or | |
| // negative, e.g., a positive BigInteger 0xFF, will return two bytes [0x00, 0xFF] | |
| bool highest_significant_bit = | |
| (static_cast<Decimal::uint128_t>(value_) >> ((valid_bytes - 1) * 8)) & 0x80; | |
| if ((positive && highest_significant_bit) || (!positive && !highest_significant_bit)) { | |
| valid_bytes += 1; | |
| } | |
| } | |
| std::vector<char> bytes(valid_bytes); | |
| memcpy(bytes.data(), &value_, valid_bytes); | |
| if (SystemByteOrder() == ByteOrder::PAIMON_LITTLE_ENDIAN) { | |
| std::reverse(bytes.data(), bytes.data() + bytes.size()); | |
| } | |
| return bytes; | |
| } | |
| Decimal Decimal::FromUnscaledBytes(int32_t precision, int32_t scale, Bytes* bytes) { | |
| if (SystemByteOrder() == ByteOrder::PAIMON_LITTLE_ENDIAN) { | |
| std::reverse(bytes->data(), bytes->data() + bytes->size()); | |
| } | |
| Decimal::int128_t value = 0; | |
| for (size_t i = 0; i < bytes->size(); ++i) { | |
| value |= static_cast<Decimal::uint128_t>(static_cast<uint8_t>((*bytes)[i])) << (8 * i); | |
| } | |
| // for negative | |
| if ((*bytes)[bytes->size() - 1] & 0x80) { | |
| for (size_t i = bytes->size(); i < sizeof(Decimal::int128_t); ++i) { | |
| value |= static_cast<Decimal::uint128_t>(0xFFull) << (8 * i); | |
| } | |
| } | |
| return Decimal(precision, scale, value); | |
| } | |
| int32_t Decimal::clz_u128(uint128_t u) { | |
| uint64_t hi = u >> 64; | |
| if (hi != 0) { | |
| return __builtin_clzll(hi); | |
| } | |
| uint64_t lo = u; | |
| if (lo != 0) { | |
| return __builtin_clzll(lo) + 64; | |
| } | |
| return 128; | |
| } | |
| int32_t Decimal::count_leading_zero_bytes(uint128_t u) { | |
| if (u == 0) { | |
| return sizeof(u); | |
| } | |
| int32_t leading_zeros = clz_u128(u); | |
| return leading_zeros / 8; | |
| } | |
| int32_t Decimal::count_leading_all_ones_bytes(uint128_t u) { | |
| if (u == 0) { | |
| return 0; | |
| } | |
| int32_t count = 0; | |
| for (int32_t i = sizeof(uint128_t) - 1; i >= 0; i--) { | |
| if (((u >> (i * 8)) & 0xFF) == 0xFF) { | |
| count++; | |
| } else { | |
| break; | |
| } | |
| } | |
| return count; | |
| } | |
| Decimal::int128_t Decimal::DownScaleInt128(Decimal::int128_t value, int32_t scale) { | |
| while (scale > 0) { | |
| int32_t step = std::min(std::abs(scale), MAX_COMPACT_PRECISION); | |
| value /= POWERS_OF_TEN[step]; | |
| scale -= step; | |
| } | |
| return value; | |
| } | |
| Decimal::int128_t Decimal::ScaleInt128(Decimal::int128_t value, int32_t scale, bool* overflow) { | |
| *overflow = false; | |
| while (scale > 0) { | |
| int32_t step = std::min(scale, MAX_COMPACT_PRECISION); | |
| if (value > 0 && INT128_MAXIMUM_VALUE / POWERS_OF_TEN[step] < value) { | |
| *overflow = true; | |
| return INT128_MAXIMUM_VALUE; | |
| } else if (value < 0 && INT128_MINIMUM_VALUE / POWERS_OF_TEN[step] > value) { | |
| *overflow = true; | |
| return INT128_MINIMUM_VALUE; | |
| } | |
| value *= POWERS_OF_TEN[step]; | |
| scale -= step; | |
| } | |
| return value; | |
| } | |
| int32_t Decimal::CompareTo(const Decimal& other) const { | |
| auto l_value = value_; | |
| auto l_scale = scale_; | |
| auto r_value = other.value_; | |
| auto r_scale = other.scale_; | |
| bool l_positive = l_value >= 0; | |
| bool r_positive = r_value >= 0; | |
| if (l_positive && !r_positive) { | |
| return 1; | |
| } else if (!l_positive && r_positive) { | |
| return -1; | |
| } | |
| // compare integral parts | |
| Decimal::int128_t l_integral = DownScaleInt128(l_value, l_scale); | |
| Decimal::int128_t r_integral = DownScaleInt128(r_value, r_scale); | |
| if (l_integral < r_integral) { | |
| return -1; | |
| } else if (l_integral > r_integral) { | |
| return 1; | |
| } | |
| // integral parts are equal, continue comparing fractional parts | |
| // unnecessary to check overflow here because the scaled number will not | |
| // exceed original ones | |
| bool overflow = false, positive = l_value >= 0; | |
| l_value -= ScaleInt128(l_integral, l_scale, &overflow); | |
| r_value -= ScaleInt128(r_integral, r_scale, &overflow); | |
| int32_t diff = l_scale - r_scale; | |
| if (diff > 0) { | |
| r_value = ScaleInt128(r_value, diff, &overflow); | |
| if (overflow) { | |
| return positive ? -1 : 1; | |
| } | |
| } else { | |
| l_value = ScaleInt128(l_value, -diff, &overflow); | |
| if (overflow) { | |
| return positive ? 1 : -1; | |
| } | |
| } | |
| if (l_value < r_value) { | |
| return -1; | |
| } else if (l_value > r_value) { | |
| return 1; | |
| } else { | |
| return 0; | |
| } | |
| } | |
| } // namespace paimon |