| /* | |
| * 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. */ | |
| #pragma once | |
| #include <cstdint> | |
| #include <string> | |
| #include <string_view> | |
| #include "paimon/result.h" | |
| namespace paimon { | |
| /// The value type of a variant value. It is determined by the header byte but not a 1:1 mapping | |
| /// (for example, INT1/2/4/8 all map to `kLong`). | |
| enum class VariantValueType { | |
| kObject, | |
| kArray, | |
| kNull, | |
| kBoolean, | |
| kLong, | |
| kString, | |
| kDouble, | |
| kDecimal, | |
| kDate, | |
| kTimestamp, | |
| kTimestampNtz, | |
| kFloat, | |
| kBinary, | |
| kUuid, | |
| }; | |
| /// An arbitrary-precision (up to 38 digits) decimal value decoded from a variant binary. The | |
| /// numeric value is `unscaled * 10^(-scale)`. Unlike `paimon::Decimal`, `scale` may become | |
| /// negative after `StripTrailingZeros` (mirroring `java.math.BigDecimal`). | |
| struct VariantDecimal { | |
| __int128_t unscaled = 0; | |
| int32_t scale = 0; | |
| /// Number of decimal digits in the unscaled value (a value of zero has precision 1). | |
| int32_t Precision() const; | |
| /// Removes trailing zero digits from the unscaled value, increasing `10^-scale` accordingly. | |
| VariantDecimal StripTrailingZeros() const; | |
| /// Plain (non-scientific) string representation, e.g. `-12.340` or `100`. | |
| std::string ToPlainString() const; | |
| bool operator==(const VariantDecimal& other) const { | |
| return unscaled == other.unscaled && scale == other.scale; | |
| } | |
| }; | |
| /// Static functions for manipulating variant binaries. See `VariantDefs` for the binary format. | |
| class VariantBinaryUtil { | |
| public: | |
| VariantBinaryUtil() = delete; | |
| ~VariantBinaryUtil() = delete; | |
| /// The decoded layout of a variant object value. | |
| struct ObjectInfo { | |
| /// Number of object fields. | |
| int32_t num_elements; | |
| /// The integer size of the field id list. | |
| int32_t id_size; | |
| /// The integer size of the offset list. | |
| int32_t offset_size; | |
| /// The starting index of the field id list in the variant value. | |
| int32_t id_start; | |
| /// The starting index of the offset list in the variant value. | |
| int32_t offset_start; | |
| /// The starting index of field data in the variant value. | |
| int32_t data_start; | |
| }; | |
| /// The decoded layout of a variant array value. | |
| struct ArrayInfo { | |
| /// Number of array elements. | |
| int32_t num_elements; | |
| /// The integer size of the offset list. | |
| int32_t offset_size; | |
| /// The starting index of the offset list in the variant value. | |
| int32_t offset_start; | |
| /// The starting index of element data in the variant value. | |
| int32_t data_start; | |
| }; | |
| /// Creates the MALFORMED_VARIANT error. `message` describes the specific corruption for | |
| /// debugging and is appended to the error when non-empty. | |
| static Status MalformedVariant(const std::string& message = ""); | |
| static Status UnknownPrimitiveTypeInVariant(int32_t id); | |
| static Status VariantConstructorSizeLimit(); | |
| static Status UnexpectedType(VariantValueType type); | |
| /// Checks the validity of an index `pos` in a buffer of `length` bytes. Returns | |
| /// `MALFORMED_VARIANT` if it is out of bound. | |
| static Status CheckIndex(int32_t pos, int32_t length); | |
| /// Writes the least significant `num_bytes` bytes in `value` into | |
| /// `bytes[pos, pos + num_bytes)` in little endian. | |
| static void WriteLong(int64_t value, int32_t num_bytes, uint8_t* bytes, int32_t pos); | |
| /// Reads a little-endian signed long value from `bytes[pos, pos + num_bytes)`. | |
| static Result<int64_t> ReadLong(std::string_view bytes, int32_t pos, int32_t num_bytes); | |
| /// Reads a little-endian unsigned int value from `bytes[pos, pos + num_bytes)`. The value | |
| /// must fit into a non-negative int32. | |
| static Result<int32_t> ReadUnsigned(std::string_view bytes, int32_t pos, int32_t num_bytes); | |
| /// Adds a buffer-relative element `offset` to `base` in 64-bit and validates the result | |
| /// stays inside `buffer_size`, guarding the 32-bit addition against corrupted offsets. | |
| static Result<int32_t> CheckedElementPos(int32_t base, int32_t offset, size_t buffer_size) { | |
| int64_t pos = static_cast<int64_t>(base) + offset; | |
| if (pos >= static_cast<int64_t>(buffer_size)) { | |
| return MalformedVariant("element offset points outside the value buffer"); | |
| } | |
| return static_cast<int32_t>(pos); | |
| } | |
| static uint8_t PrimitiveHeader(int32_t type); | |
| static uint8_t ShortStrHeader(int32_t size); | |
| static uint8_t ObjectHeader(bool large_size, int32_t id_size, int32_t offset_size); | |
| static uint8_t ArrayHeader(bool large_size, int32_t offset_size); | |
| /// Gets the type info bits from the variant value `value[pos...]`. | |
| static Result<int32_t> GetTypeInfo(std::string_view value, int32_t pos); | |
| /// Gets the value type of the variant value `value[pos...]`. It is only legal to call `Get*` | |
| /// if `GetType` returns the corresponding type (for example, it is only legal to call | |
| /// `GetLong` if `GetType` returns `kLong`). | |
| static Result<VariantValueType> GetType(std::string_view value, int32_t pos); | |
| /// Computes the size in bytes of the variant value `value[pos...]`. `value.size() - pos` is | |
| /// an upper bound of the size, but the actual size can be smaller. | |
| static Result<int32_t> ValueSize(std::string_view value, int32_t pos); | |
| static Result<bool> GetBoolean(std::string_view value, int32_t pos); | |
| /// Gets a long value from the variant value `value[pos...]`. It is only legal to call it if | |
| /// `GetType` returns one of `kLong/kDate/kTimestamp/kTimestampNtz`. If the type is `kDate`, | |
| /// the return value is guaranteed to fit into an int32 and represents the number of days from | |
| /// the Unix epoch. If the type is `kTimestamp/kTimestampNtz`, the return value represents the | |
| /// number of microseconds from the Unix epoch. | |
| static Result<int64_t> GetLong(std::string_view value, int32_t pos); | |
| static Result<double> GetDouble(std::string_view value, int32_t pos); | |
| /// Gets a decimal value from the variant value `value[pos...]`, keeping the stored scale. | |
| static Result<VariantDecimal> GetDecimalWithOriginalScale(std::string_view value, int32_t pos); | |
| /// Gets a decimal value from the variant value `value[pos...]` with trailing zeros stripped. | |
| static Result<VariantDecimal> GetDecimal(std::string_view value, int32_t pos); | |
| static Result<float> GetFloat(std::string_view value, int32_t pos); | |
| /// Gets a binary value from the variant value `value[pos...]`. The returned view aliases | |
| /// `value` and remains valid only as long as the underlying buffer. | |
| static Result<std::string_view> GetBinary(std::string_view value, int32_t pos); | |
| /// Gets a string value from the variant value `value[pos...]`. The returned view aliases | |
| /// `value` and remains valid only as long as the underlying buffer. | |
| static Result<std::string_view> GetString(std::string_view value, int32_t pos); | |
| /// Gets a UUID value (16 bytes, big-endian) from the variant value `value[pos...]`. The | |
| /// returned view aliases `value`. | |
| static Result<std::string_view> GetUuid(std::string_view value, int32_t pos); | |
| /// Formats a 16-byte big-endian UUID as the canonical lower-case string, e.g. | |
| /// `123e4567-e89b-12d3-a456-426614174000`. | |
| static std::string UuidToString(std::string_view uuid_bytes); | |
| /// Decodes the layout of the variant object value `value[pos...]`. | |
| static Result<ObjectInfo> GetObjectInfo(std::string_view value, int32_t pos); | |
| /// Decodes the layout of the variant array value `value[pos...]`. | |
| static Result<ArrayInfo> GetArrayInfo(std::string_view value, int32_t pos); | |
| /// Gets the key at `id` in the variant metadata. An out-of-bound `id` is considered a | |
| /// malformed variant because it is read from the corresponding variant value. | |
| static Result<std::string_view> GetMetadataKey(std::string_view metadata, int32_t id); | |
| }; | |
| } // namespace paimon |