blob: 01a55f4be69703fd7e4d60dc778ec386f2e856af [file]
/*
* 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