| /* |
| * 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 |