| /* |
| * 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. |
| */ |
| |
| /* This file is based on source code from the Spark Project (http://spark.apache.org/), licensed |
| * by the Apache Software Foundation (ASF) under the Apache License, Version 2.0. See the NOTICE |
| * file distributed with this work for additional information regarding copyright ownership. */ |
| |
| #include "paimon/common/data/variant/variant_binary_util.h" |
| |
| #include <algorithm> |
| #include <cstring> |
| #include <limits> |
| |
| #include "fmt/format.h" |
| #include "paimon/common/data/variant/variant_defs.h" |
| |
| namespace paimon { |
| |
| int32_t VariantDecimal::Precision() const { |
| __uint128_t abs_value = |
| unscaled < 0 ? -static_cast<__uint128_t>(unscaled) : static_cast<__uint128_t>(unscaled); |
| int32_t digits = 1; |
| while (abs_value >= 10) { |
| abs_value /= 10; |
| ++digits; |
| } |
| return digits; |
| } |
| |
| VariantDecimal VariantDecimal::StripTrailingZeros() const { |
| VariantDecimal result = *this; |
| if (result.unscaled == 0) { |
| result.scale = 0; |
| return result; |
| } |
| while (result.unscaled % 10 == 0) { |
| result.unscaled /= 10; |
| --result.scale; |
| } |
| return result; |
| } |
| |
| std::string VariantDecimal::ToPlainString() const { |
| __uint128_t abs_value = |
| unscaled < 0 ? -static_cast<__uint128_t>(unscaled) : static_cast<__uint128_t>(unscaled); |
| std::string digits; |
| if (abs_value == 0) { |
| digits = "0"; |
| } else { |
| while (abs_value > 0) { |
| digits.push_back(static_cast<char>('0' + static_cast<int32_t>(abs_value % 10))); |
| abs_value /= 10; |
| } |
| std::reverse(digits.begin(), digits.end()); |
| } |
| std::string result; |
| if (unscaled < 0) { |
| result.push_back('-'); |
| } |
| if (scale <= 0) { |
| result.append(digits); |
| result.append(static_cast<size_t>(-scale), '0'); |
| } else if (static_cast<size_t>(scale) < digits.size()) { |
| result.append(digits, 0, digits.size() - scale); |
| result.push_back('.'); |
| result.append(digits, digits.size() - scale, scale); |
| } else { |
| result.append("0."); |
| result.append(static_cast<size_t>(scale) - digits.size(), '0'); |
| result.append(digits); |
| } |
| return result; |
| } |
| |
| Status VariantBinaryUtil::MalformedVariant(const std::string& message) { |
| if (message.empty()) { |
| return Status::Invalid("MALFORMED_VARIANT"); |
| } |
| return Status::Invalid(fmt::format("MALFORMED_VARIANT: {}", message)); |
| } |
| |
| Status VariantBinaryUtil::UnknownPrimitiveTypeInVariant(int32_t id) { |
| return Status::Invalid(fmt::format("UNKNOWN_PRIMITIVE_TYPE_IN_VARIANT, id: {}", id)); |
| } |
| |
| Status VariantBinaryUtil::VariantConstructorSizeLimit() { |
| return Status::Invalid("VARIANT_CONSTRUCTOR_SIZE_LIMIT"); |
| } |
| |
| Status VariantBinaryUtil::UnexpectedType(VariantValueType type) { |
| static constexpr const char* kTypeNames[] = { |
| "OBJECT", "ARRAY", "NULL", "BOOLEAN", "LONG", "STRING", "DOUBLE", |
| "DECIMAL", "DATE", "TIMESTAMP", "TIMESTAMP_NTZ", "FLOAT", "BINARY", "UUID"}; |
| return Status::Invalid( |
| fmt::format("Expect type to be {}", kTypeNames[static_cast<int32_t>(type)])); |
| } |
| |
| Status VariantBinaryUtil::CheckIndex(int32_t pos, int32_t length) { |
| if (pos < 0 || pos >= length) { |
| return MalformedVariant( |
| fmt::format("index {} is out of bounds for a buffer of {} bytes", pos, length)); |
| } |
| return Status::OK(); |
| } |
| |
| void VariantBinaryUtil::WriteLong(int64_t value, int32_t num_bytes, uint8_t* bytes, int32_t pos) { |
| for (int32_t i = 0; i < num_bytes; ++i) { |
| bytes[pos + i] = static_cast<uint8_t>((static_cast<uint64_t>(value) >> (8 * i)) & 0xFF); |
| } |
| } |
| |
| Result<int64_t> VariantBinaryUtil::ReadLong(std::string_view bytes, int32_t pos, |
| int32_t num_bytes) { |
| auto length = static_cast<int32_t>(bytes.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos + num_bytes - 1, length)); |
| uint64_t result = 0; |
| // All bytes except the most significant byte should be unsign-extended and shifted. The most |
| // significant byte should be sign-extended. |
| for (int32_t i = 0; i < num_bytes - 1; ++i) { |
| uint64_t unsigned_byte_value = static_cast<uint8_t>(bytes[pos + i]); |
| result |= unsigned_byte_value << (8 * i); |
| } |
| int64_t signed_byte_value = static_cast<int8_t>(bytes[pos + num_bytes - 1]); |
| result |= static_cast<uint64_t>(signed_byte_value) << (8 * (num_bytes - 1)); |
| return static_cast<int64_t>(result); |
| } |
| |
| Result<int32_t> VariantBinaryUtil::ReadUnsigned(std::string_view bytes, int32_t pos, |
| int32_t num_bytes) { |
| auto length = static_cast<int32_t>(bytes.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos + num_bytes - 1, length)); |
| int64_t result = 0; |
| // Similar to the `ReadLong` loop, but all bytes should be unsign-extended. |
| for (int32_t i = 0; i < num_bytes; ++i) { |
| int64_t unsigned_byte_value = static_cast<uint8_t>(bytes[pos + i]); |
| result |= unsigned_byte_value << (8 * i); |
| } |
| if (result < 0 || result > std::numeric_limits<int32_t>::max()) { |
| return MalformedVariant(fmt::format("unsigned value {} does not fit into int32", result)); |
| } |
| return static_cast<int32_t>(result); |
| } |
| |
| uint8_t VariantBinaryUtil::PrimitiveHeader(int32_t type) { |
| return static_cast<uint8_t>(type << 2 | VariantDefs::kPrimitive); |
| } |
| |
| uint8_t VariantBinaryUtil::ShortStrHeader(int32_t size) { |
| return static_cast<uint8_t>(size << 2 | VariantDefs::kShortStr); |
| } |
| |
| uint8_t VariantBinaryUtil::ObjectHeader(bool large_size, int32_t id_size, int32_t offset_size) { |
| return static_cast<uint8_t>(((large_size ? 1 : 0) << (VariantDefs::kBasicTypeBits + 4)) | |
| ((id_size - 1) << (VariantDefs::kBasicTypeBits + 2)) | |
| ((offset_size - 1) << VariantDefs::kBasicTypeBits) | |
| VariantDefs::kObject); |
| } |
| |
| uint8_t VariantBinaryUtil::ArrayHeader(bool large_size, int32_t offset_size) { |
| return static_cast<uint8_t>(((large_size ? 1 : 0) << (VariantDefs::kBasicTypeBits + 2)) | |
| ((offset_size - 1) << VariantDefs::kBasicTypeBits) | |
| VariantDefs::kArray); |
| } |
| |
| Result<int32_t> VariantBinaryUtil::GetTypeInfo(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| return (static_cast<uint8_t>(value[pos]) >> VariantDefs::kBasicTypeBits) & |
| VariantDefs::kTypeInfoMask; |
| } |
| |
| Result<VariantValueType> VariantBinaryUtil::GetType(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| switch (basic_type) { |
| case VariantDefs::kShortStr: |
| return VariantValueType::kString; |
| case VariantDefs::kObject: |
| return VariantValueType::kObject; |
| case VariantDefs::kArray: |
| return VariantValueType::kArray; |
| default: |
| switch (type_info) { |
| case VariantDefs::kNull: |
| return VariantValueType::kNull; |
| case VariantDefs::kTrue: |
| case VariantDefs::kFalse: |
| return VariantValueType::kBoolean; |
| case VariantDefs::kInt1: |
| case VariantDefs::kInt2: |
| case VariantDefs::kInt4: |
| case VariantDefs::kInt8: |
| return VariantValueType::kLong; |
| case VariantDefs::kDouble: |
| return VariantValueType::kDouble; |
| case VariantDefs::kDecimal4: |
| case VariantDefs::kDecimal8: |
| case VariantDefs::kDecimal16: |
| return VariantValueType::kDecimal; |
| case VariantDefs::kDate: |
| return VariantValueType::kDate; |
| case VariantDefs::kTimestamp: |
| return VariantValueType::kTimestamp; |
| case VariantDefs::kTimestampNtz: |
| return VariantValueType::kTimestampNtz; |
| case VariantDefs::kFloat: |
| return VariantValueType::kFloat; |
| case VariantDefs::kBinary: |
| return VariantValueType::kBinary; |
| case VariantDefs::kLongStr: |
| return VariantValueType::kString; |
| case VariantDefs::kUuid: |
| return VariantValueType::kUuid; |
| default: |
| return UnknownPrimitiveTypeInVariant(type_info); |
| } |
| } |
| } |
| |
| Result<int32_t> VariantBinaryUtil::ValueSize(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| switch (basic_type) { |
| case VariantDefs::kShortStr: |
| return 1 + type_info; |
| case VariantDefs::kObject: { |
| PAIMON_ASSIGN_OR_RAISE(ObjectInfo info, GetObjectInfo(value, pos)); |
| PAIMON_ASSIGN_OR_RAISE( |
| int32_t data_size, |
| ReadUnsigned(value, info.offset_start + info.num_elements * info.offset_size, |
| info.offset_size)); |
| return info.data_start - pos + data_size; |
| } |
| case VariantDefs::kArray: { |
| PAIMON_ASSIGN_OR_RAISE(ArrayInfo info, GetArrayInfo(value, pos)); |
| PAIMON_ASSIGN_OR_RAISE( |
| int32_t data_size, |
| ReadUnsigned(value, info.offset_start + info.num_elements * info.offset_size, |
| info.offset_size)); |
| return info.data_start - pos + data_size; |
| } |
| default: |
| switch (type_info) { |
| case VariantDefs::kNull: |
| case VariantDefs::kTrue: |
| case VariantDefs::kFalse: |
| return 1; |
| case VariantDefs::kInt1: |
| return 2; |
| case VariantDefs::kInt2: |
| return 3; |
| case VariantDefs::kInt4: |
| case VariantDefs::kDate: |
| case VariantDefs::kFloat: |
| return 5; |
| case VariantDefs::kInt8: |
| case VariantDefs::kDouble: |
| case VariantDefs::kTimestamp: |
| case VariantDefs::kTimestampNtz: |
| return 9; |
| case VariantDefs::kDecimal4: |
| return 6; |
| case VariantDefs::kDecimal8: |
| return 10; |
| case VariantDefs::kDecimal16: |
| return 18; |
| case VariantDefs::kBinary: |
| case VariantDefs::kLongStr: { |
| PAIMON_ASSIGN_OR_RAISE(int32_t data_size, |
| ReadUnsigned(value, pos + 1, VariantDefs::kU32Size)); |
| return 1 + VariantDefs::kU32Size + data_size; |
| } |
| case VariantDefs::kUuid: |
| return 17; |
| default: |
| return UnknownPrimitiveTypeInVariant(type_info); |
| } |
| } |
| } |
| |
| Result<bool> VariantBinaryUtil::GetBoolean(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive || |
| (type_info != VariantDefs::kTrue && type_info != VariantDefs::kFalse)) { |
| return UnexpectedType(VariantValueType::kBoolean); |
| } |
| return type_info == VariantDefs::kTrue; |
| } |
| |
| Result<int64_t> VariantBinaryUtil::GetLong(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| constexpr const char* kExceptionMessage = "Expect type to be LONG/DATE/TIMESTAMP/TIMESTAMP_NTZ"; |
| if (basic_type != VariantDefs::kPrimitive) { |
| return Status::Invalid(kExceptionMessage); |
| } |
| switch (type_info) { |
| case VariantDefs::kInt1: |
| return ReadLong(value, pos + 1, 1); |
| case VariantDefs::kInt2: |
| return ReadLong(value, pos + 1, 2); |
| case VariantDefs::kInt4: |
| case VariantDefs::kDate: |
| return ReadLong(value, pos + 1, 4); |
| case VariantDefs::kInt8: |
| case VariantDefs::kTimestamp: |
| case VariantDefs::kTimestampNtz: |
| return ReadLong(value, pos + 1, 8); |
| default: |
| return Status::Invalid(kExceptionMessage); |
| } |
| } |
| |
| Result<double> VariantBinaryUtil::GetDouble(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive || type_info != VariantDefs::kDouble) { |
| return UnexpectedType(VariantValueType::kDouble); |
| } |
| PAIMON_ASSIGN_OR_RAISE(int64_t bits, ReadLong(value, pos + 1, 8)); |
| double result; |
| memcpy(&result, &bits, sizeof(result)); |
| return result; |
| } |
| |
| namespace { |
| // Checks whether the precision and scale of the decimal are within the limit. |
| Status CheckDecimal(const VariantDecimal& d, int32_t max_precision) { |
| if (d.Precision() > max_precision || d.scale > max_precision) { |
| return VariantBinaryUtil::MalformedVariant( |
| fmt::format("decimal precision {} or scale {} exceeds the maximum precision {}", |
| d.Precision(), d.scale, max_precision)); |
| } |
| return Status::OK(); |
| } |
| } // namespace |
| |
| Result<VariantDecimal> VariantBinaryUtil::GetDecimalWithOriginalScale(std::string_view value, |
| int32_t pos) { |
| auto length = static_cast<int32_t>(value.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive) { |
| return UnexpectedType(VariantValueType::kDecimal); |
| } |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos + 1, length)); |
| // Interpret the scale byte as unsigned. If it is a negative byte, the unsigned value must be |
| // greater than `kMaxDecimal16Precision` and will trigger an error in `CheckDecimal`. |
| int32_t scale = static_cast<uint8_t>(value[pos + 1]); |
| VariantDecimal result; |
| result.scale = scale; |
| switch (type_info) { |
| case VariantDefs::kDecimal4: { |
| PAIMON_ASSIGN_OR_RAISE(int64_t unscaled, ReadLong(value, pos + 2, 4)); |
| result.unscaled = unscaled; |
| PAIMON_RETURN_NOT_OK(CheckDecimal(result, VariantDefs::kMaxDecimal4Precision)); |
| break; |
| } |
| case VariantDefs::kDecimal8: { |
| PAIMON_ASSIGN_OR_RAISE(int64_t unscaled, ReadLong(value, pos + 2, 8)); |
| result.unscaled = unscaled; |
| PAIMON_RETURN_NOT_OK(CheckDecimal(result, VariantDefs::kMaxDecimal8Precision)); |
| break; |
| } |
| case VariantDefs::kDecimal16: { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos + 17, length)); |
| __uint128_t unscaled = 0; |
| for (int32_t i = 0; i < 16; ++i) { |
| unscaled |= static_cast<__uint128_t>(static_cast<uint8_t>(value[pos + 2 + i])) |
| << (8 * i); |
| } |
| result.unscaled = static_cast<__int128_t>(unscaled); |
| PAIMON_RETURN_NOT_OK(CheckDecimal(result, VariantDefs::kMaxDecimal16Precision)); |
| break; |
| } |
| default: |
| return UnexpectedType(VariantValueType::kDecimal); |
| } |
| return result; |
| } |
| |
| Result<VariantDecimal> VariantBinaryUtil::GetDecimal(std::string_view value, int32_t pos) { |
| PAIMON_ASSIGN_OR_RAISE(VariantDecimal decimal, GetDecimalWithOriginalScale(value, pos)); |
| return decimal.StripTrailingZeros(); |
| } |
| |
| Result<float> VariantBinaryUtil::GetFloat(std::string_view value, int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive || type_info != VariantDefs::kFloat) { |
| return UnexpectedType(VariantValueType::kFloat); |
| } |
| PAIMON_ASSIGN_OR_RAISE(int64_t bits, ReadLong(value, pos + 1, 4)); |
| auto int_bits = static_cast<int32_t>(bits); |
| float result; |
| memcpy(&result, &int_bits, sizeof(result)); |
| return result; |
| } |
| |
| Result<std::string_view> VariantBinaryUtil::GetBinary(std::string_view value, int32_t pos) { |
| auto length = static_cast<int32_t>(value.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive || type_info != VariantDefs::kBinary) { |
| return UnexpectedType(VariantValueType::kBinary); |
| } |
| int32_t start = pos + 1 + VariantDefs::kU32Size; |
| PAIMON_ASSIGN_OR_RAISE(int32_t data_size, ReadUnsigned(value, pos + 1, VariantDefs::kU32Size)); |
| if (data_size > 0) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(start + data_size - 1, length)); |
| } |
| return value.substr(start, data_size); |
| } |
| |
| Result<std::string_view> VariantBinaryUtil::GetString(std::string_view value, int32_t pos) { |
| auto length = static_cast<int32_t>(value.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type == VariantDefs::kShortStr || |
| (basic_type == VariantDefs::kPrimitive && type_info == VariantDefs::kLongStr)) { |
| int32_t start; |
| int32_t str_size; |
| if (basic_type == VariantDefs::kShortStr) { |
| start = pos + 1; |
| str_size = type_info; |
| } else { |
| start = pos + 1 + VariantDefs::kU32Size; |
| PAIMON_ASSIGN_OR_RAISE(str_size, ReadUnsigned(value, pos + 1, VariantDefs::kU32Size)); |
| } |
| if (str_size > 0) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(start + str_size - 1, length)); |
| } |
| return value.substr(start, str_size); |
| } |
| return UnexpectedType(VariantValueType::kString); |
| } |
| |
| Result<std::string_view> VariantBinaryUtil::GetUuid(std::string_view value, int32_t pos) { |
| auto length = static_cast<int32_t>(value.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, length)); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kPrimitive || type_info != VariantDefs::kUuid) { |
| return UnexpectedType(VariantValueType::kUuid); |
| } |
| int32_t start = pos + 1; |
| PAIMON_RETURN_NOT_OK(CheckIndex(start + 15, length)); |
| return value.substr(start, 16); |
| } |
| |
| std::string VariantBinaryUtil::UuidToString(std::string_view uuid_bytes) { |
| constexpr char kHexDigits[] = "0123456789abcdef"; |
| std::string result; |
| result.reserve(36); |
| for (size_t i = 0; i < 16; ++i) { |
| if (i == 4 || i == 6 || i == 8 || i == 10) { |
| result.push_back('-'); |
| } |
| auto byte = static_cast<uint8_t>(uuid_bytes[i]); |
| result.push_back(kHexDigits[byte >> 4]); |
| result.push_back(kHexDigits[byte & 0xF]); |
| } |
| return result; |
| } |
| |
| Result<VariantBinaryUtil::ObjectInfo> VariantBinaryUtil::GetObjectInfo(std::string_view value, |
| int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kObject) { |
| return UnexpectedType(VariantValueType::kObject); |
| } |
| // Refer to the comment of the `VariantDefs::kObject` constant for the details of the object |
| // header encoding. Suppose `type_info` has a bit representation of 0_b4_b3b2_b1b0, the |
| // following line extracts b4 to determine whether the object uses a 1/4-byte size. |
| bool large_size = ((type_info >> 4) & 0x1) != 0; |
| int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1; |
| ObjectInfo info; |
| PAIMON_ASSIGN_OR_RAISE(info.num_elements, ReadUnsigned(value, pos + 1, size_bytes)); |
| // Extracts b3b2 to determine the integer size of the field id list. |
| info.id_size = ((type_info >> 2) & 0x3) + 1; |
| // Extracts b1b0 to determine the integer size of the offset list. |
| info.offset_size = (type_info & 0x3) + 1; |
| info.id_start = pos + 1 + size_bytes; |
| // A corrupted variant can claim a near-INT32_MAX element count; compute the layout in |
| // 64-bit and bound it by the buffer before 32-bit arithmetic could overflow. |
| int64_t offset_start = static_cast<int64_t>(info.id_start) + |
| static_cast<int64_t>(info.num_elements) * info.id_size; |
| int64_t data_start = |
| offset_start + (static_cast<int64_t>(info.num_elements) + 1) * info.offset_size; |
| if (data_start > static_cast<int64_t>(value.size())) { |
| return MalformedVariant("object layout exceeds the value buffer"); |
| } |
| info.offset_start = static_cast<int32_t>(offset_start); |
| info.data_start = static_cast<int32_t>(data_start); |
| return info; |
| } |
| |
| Result<VariantBinaryUtil::ArrayInfo> VariantBinaryUtil::GetArrayInfo(std::string_view value, |
| int32_t pos) { |
| PAIMON_RETURN_NOT_OK(CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| auto header = static_cast<uint8_t>(value[pos]); |
| int32_t basic_type = header & VariantDefs::kBasicTypeMask; |
| int32_t type_info = (header >> VariantDefs::kBasicTypeBits) & VariantDefs::kTypeInfoMask; |
| if (basic_type != VariantDefs::kArray) { |
| return UnexpectedType(VariantValueType::kArray); |
| } |
| // Suppose `type_info` has a bit representation of 000_b2_b1b0, the following line extracts b2 |
| // to determine whether the array uses a 1/4-byte size. |
| bool large_size = ((type_info >> 2) & 0x1) != 0; |
| int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1; |
| ArrayInfo info; |
| PAIMON_ASSIGN_OR_RAISE(info.num_elements, ReadUnsigned(value, pos + 1, size_bytes)); |
| // Extracts b1b0 to determine the integer size of the offset list. |
| info.offset_size = (type_info & 0x3) + 1; |
| info.offset_start = pos + 1 + size_bytes; |
| // See GetObjectInfo: bound the 64-bit layout by the buffer before 32-bit overflow. |
| int64_t data_start = static_cast<int64_t>(info.offset_start) + |
| (static_cast<int64_t>(info.num_elements) + 1) * info.offset_size; |
| if (data_start > static_cast<int64_t>(value.size())) { |
| return MalformedVariant("array layout exceeds the value buffer"); |
| } |
| info.data_start = static_cast<int32_t>(data_start); |
| return info; |
| } |
| |
| Result<std::string_view> VariantBinaryUtil::GetMetadataKey(std::string_view metadata, int32_t id) { |
| auto length = static_cast<int32_t>(metadata.size()); |
| PAIMON_RETURN_NOT_OK(CheckIndex(0, length)); |
| // Extracts the highest 2 bits in the metadata header to determine the integer size of the |
| // offset list. |
| int32_t offset_size = ((static_cast<uint8_t>(metadata[0]) >> 6) & 0x3) + 1; |
| PAIMON_ASSIGN_OR_RAISE(int32_t dict_size, ReadUnsigned(metadata, 1, offset_size)); |
| if (id >= dict_size) { |
| return MalformedVariant(fmt::format( |
| "metadata key id {} is out of bounds for a dictionary of {} keys", id, dict_size)); |
| } |
| // There are a header byte, a `dict_size` with `offset_size` bytes, and `(dict_size + 1)` |
| // offsets before the string data. |
| // Bound the offset-table layout in 64-bit before 32-bit arithmetic could overflow on a |
| // corrupted dictionary size. |
| int64_t string_start64 = 1 + (static_cast<int64_t>(dict_size) + 2) * offset_size; |
| if (string_start64 > static_cast<int64_t>(length)) { |
| return MalformedVariant("metadata dictionary layout exceeds the metadata buffer"); |
| } |
| auto string_start = static_cast<int32_t>(string_start64); |
| PAIMON_ASSIGN_OR_RAISE(int32_t offset, |
| ReadUnsigned(metadata, 1 + (id + 1) * offset_size, offset_size)); |
| PAIMON_ASSIGN_OR_RAISE(int32_t next_offset, |
| ReadUnsigned(metadata, 1 + (id + 2) * offset_size, offset_size)); |
| if (offset > next_offset) { |
| return MalformedVariant("metadata key offsets are not monotonic"); |
| } |
| if (static_cast<int64_t>(string_start) + next_offset > static_cast<int64_t>(length)) { |
| return MalformedVariant("metadata key data exceeds the metadata buffer"); |
| } |
| return metadata.substr(string_start + offset, next_offset - offset); |
| } |
| |
| } // namespace paimon |