| /* |
| * 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_builder.h" |
| |
| #include <algorithm> |
| #include <charconv> |
| #include <cstdlib> |
| #include <cstring> |
| |
| #include "fmt/format.h" |
| #include "paimon/common/data/variant/variant_defs.h" |
| #include "rapidjson/error/en.h" |
| #include "rapidjson/memorystream.h" |
| #include "rapidjson/reader.h" |
| |
| namespace paimon { |
| |
| namespace { |
| |
| // A rapidjson SAX handler that feeds parsed JSON events into a `VariantBuilder`, mirroring |
| // `GenericVariantBuilder.buildJson` in the Java implementation. Numbers are delivered as raw |
| // text (`kParseNumbersAsStringsFlag`) so that exact decimal semantics match Jackson's. |
| class JsonToVariantHandler |
| : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, JsonToVariantHandler> { |
| public: |
| explicit JsonToVariantHandler(VariantBuilder* builder) : builder_(builder) {} |
| |
| bool Null() { |
| BeforeValue(); |
| return Ok(builder_->AppendNull()); |
| } |
| |
| bool Bool(bool b) { |
| BeforeValue(); |
| return Ok(builder_->AppendBoolean(b)); |
| } |
| |
| bool RawNumber(const char* str, rapidjson::SizeType length, bool /*copy*/) { |
| BeforeValue(); |
| return Ok(AppendNumber(std::string_view(str, length))); |
| } |
| |
| bool String(const char* str, rapidjson::SizeType length, bool /*copy*/) { |
| BeforeValue(); |
| return Ok(builder_->AppendString(std::string_view(str, length))); |
| } |
| |
| bool StartObject() { |
| BeforeValue(); |
| contexts_.emplace_back(Context{true, builder_->GetWritePos(), {}, {}, {}}); |
| return true; |
| } |
| |
| bool Key(const char* str, rapidjson::SizeType length, bool /*copy*/) { |
| contexts_.back().pending_key.assign(str, length); |
| return true; |
| } |
| |
| bool EndObject(rapidjson::SizeType /*member_count*/) { |
| Context context = std::move(contexts_.back()); |
| contexts_.pop_back(); |
| return Ok(builder_->FinishWritingObject(context.start, &context.fields)); |
| } |
| |
| bool StartArray() { |
| BeforeValue(); |
| contexts_.emplace_back(Context{false, builder_->GetWritePos(), {}, {}, {}}); |
| return true; |
| } |
| |
| bool EndArray(rapidjson::SizeType /*element_count*/) { |
| Context context = std::move(contexts_.back()); |
| contexts_.pop_back(); |
| return Ok(builder_->FinishWritingArray(context.start, context.offsets)); |
| } |
| |
| const Status& status() const { |
| return status_; |
| } |
| |
| private: |
| struct Context { |
| bool is_object; |
| int32_t start; |
| std::vector<VariantBuilder::FieldEntry> fields; |
| std::vector<int32_t> offsets; |
| std::string pending_key; |
| }; |
| |
| bool Ok(const Status& status) { |
| if (!status.ok()) { |
| status_ = status; |
| return false; |
| } |
| return true; |
| } |
| |
| // Records the offset of the value that is about to be appended in the enclosing container. |
| void BeforeValue() { |
| if (contexts_.empty()) { |
| return; |
| } |
| Context& top = contexts_.back(); |
| int32_t offset = builder_->GetWritePos() - top.start; |
| if (top.is_object) { |
| int32_t id = builder_->AddKey(top.pending_key); |
| top.fields.emplace_back(top.pending_key, id, offset); |
| } else { |
| top.offsets.push_back(offset); |
| } |
| } |
| |
| // Mirrors the Java number handling: integers that fit in a long are appended as long; |
| // everything else is first tried as an exact decimal and falls back to double. |
| Status AppendNumber(std::string_view text) { |
| bool integral = true; |
| for (char c : text) { |
| if (c != '-' && !(c >= '0' && c <= '9')) { |
| integral = false; |
| break; |
| } |
| } |
| if (integral) { |
| int64_t long_value = 0; |
| auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), long_value); |
| if (ec == std::errc() && ptr == text.data() + text.size()) { |
| return builder_->AppendLong(long_value); |
| } |
| } |
| PAIMON_ASSIGN_OR_RAISE(bool appended, TryAppendDecimal(text)); |
| if (appended) { |
| return Status::OK(); |
| } |
| char* end = nullptr; |
| std::string text_copy(text); |
| double double_value = std::strtod(text_copy.c_str(), &end); |
| if (end != text_copy.c_str() + text_copy.size()) { |
| return Status::Invalid(fmt::format("Invalid JSON number: {}", text)); |
| } |
| return builder_->AppendDouble(double_value); |
| } |
| |
| // Tries to append a JSON number as an exact decimal. Returns whether it succeeded. The input |
| // must only use the decimal format (an integer value with an optional '.' in it) and must not |
| // use scientific notation. It also must fit into the precision limitation of decimal types. |
| Result<bool> TryAppendDecimal(std::string_view text) { |
| for (char c : text) { |
| if (c != '-' && c != '.' && !(c >= '0' && c <= '9')) { |
| return false; |
| } |
| } |
| bool negative = false; |
| size_t i = 0; |
| if (i < text.size() && text[i] == '-') { |
| negative = true; |
| ++i; |
| } |
| __int128_t unscaled = 0; |
| int32_t scale = 0; |
| int32_t significant_digits = 0; |
| bool seen_point = false; |
| bool seen_nonzero = false; |
| for (; i < text.size(); ++i) { |
| char c = text[i]; |
| if (c == '.') { |
| seen_point = true; |
| continue; |
| } |
| if (seen_point) { |
| ++scale; |
| } |
| if (c != '0' || seen_nonzero) { |
| seen_nonzero = true; |
| ++significant_digits; |
| } |
| if (significant_digits > VariantDefs::kMaxDecimal16Precision) { |
| return false; |
| } |
| unscaled = unscaled * 10 + (c - '0'); |
| } |
| if (scale > VariantDefs::kMaxDecimal16Precision) { |
| return false; |
| } |
| VariantDecimal decimal; |
| decimal.unscaled = negative ? -unscaled : unscaled; |
| decimal.scale = scale; |
| PAIMON_RETURN_NOT_OK(builder_->AppendDecimal(decimal)); |
| return true; |
| } |
| |
| VariantBuilder* builder_; |
| std::vector<Context> contexts_; |
| Status status_; |
| }; |
| |
| } // namespace |
| |
| Result<std::shared_ptr<GenericVariant>> VariantBuilder::ParseJson( |
| std::string_view json, bool allow_duplicate_keys, const std::shared_ptr<MemoryPool>& pool) { |
| VariantBuilder builder(allow_duplicate_keys); |
| JsonToVariantHandler handler(&builder); |
| rapidjson::Reader reader; |
| rapidjson::MemoryStream stream(json.data(), json.size()); |
| rapidjson::ParseResult result = |
| reader.Parse<rapidjson::kParseNumbersAsStringsFlag>(stream, handler); |
| if (!result) { |
| if (!handler.status().ok()) { |
| return handler.status(); |
| } |
| return Status::Invalid(fmt::format("Failed to parse JSON: {} (at offset {})", |
| rapidjson::GetParseError_En(result.Code()), |
| result.Offset())); |
| } |
| return builder.Build(pool); |
| } |
| |
| Result<std::shared_ptr<GenericVariant>> VariantBuilder::Build( |
| const std::shared_ptr<MemoryPool>& pool) { |
| auto num_keys = static_cast<int32_t>(dictionary_keys_.size()); |
| // Use int64 to avoid overflow in accumulating lengths. |
| int64_t dictionary_string_size = 0; |
| for (const std::string& key : dictionary_keys_) { |
| dictionary_string_size += static_cast<int64_t>(key.size()); |
| } |
| // Determine the number of bytes required per offset entry. The largest offset is the |
| // one-past-the-end value, which is the total string size. It's very unlikely that the number |
| // of keys could be larger, but incorporate that into the calculation in case of pathological |
| // data. |
| int64_t max_size = std::max(dictionary_string_size, static_cast<int64_t>(num_keys)); |
| if (max_size > VariantDefs::kSizeLimit) { |
| return Status::Invalid("VARIANT_SIZE_LIMIT"); |
| } |
| int32_t offset_size = GetIntegerSize(static_cast<int32_t>(max_size)); |
| |
| int32_t offset_start = 1 + offset_size; |
| int32_t string_start = offset_start + (num_keys + 1) * offset_size; |
| int64_t metadata_size = string_start + dictionary_string_size; |
| if (metadata_size > VariantDefs::kSizeLimit) { |
| return Status::Invalid("VARIANT_SIZE_LIMIT"); |
| } |
| |
| std::shared_ptr<Bytes> metadata = |
| Bytes::AllocateBytes(static_cast<size_t>(metadata_size), pool.get()); |
| auto* metadata_data = reinterpret_cast<uint8_t*>(metadata->data()); |
| int32_t header_byte = VariantDefs::kVersion | ((offset_size - 1) << 6); |
| VariantBinaryUtil::WriteLong(header_byte, 1, metadata_data, 0); |
| VariantBinaryUtil::WriteLong(num_keys, offset_size, metadata_data, 1); |
| int32_t current_offset = 0; |
| for (int32_t i = 0; i < num_keys; ++i) { |
| VariantBinaryUtil::WriteLong(current_offset, offset_size, metadata_data, |
| offset_start + i * offset_size); |
| const std::string& key = dictionary_keys_[i]; |
| memcpy(metadata_data + string_start + current_offset, key.data(), key.size()); |
| current_offset += static_cast<int32_t>(key.size()); |
| } |
| VariantBinaryUtil::WriteLong(current_offset, offset_size, metadata_data, |
| offset_start + num_keys * offset_size); |
| |
| std::shared_ptr<Bytes> value = |
| Bytes::AllocateBytes(static_cast<size_t>(write_pos_), pool.get()); |
| memcpy(value->data(), write_buffer_.data(), static_cast<size_t>(write_pos_)); |
| return GenericVariant::Create(std::move(value), std::move(metadata)); |
| } |
| |
| Status VariantBuilder::AppendString(std::string_view str) { |
| bool long_str = static_cast<int32_t>(str.size()) > VariantDefs::kMaxShortStrSize; |
| PAIMON_RETURN_NOT_OK(CheckCapacity((long_str ? 1 + VariantDefs::kU32Size : 1) + |
| static_cast<int32_t>(str.size()))); |
| if (long_str) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kLongStr); |
| VariantBinaryUtil::WriteLong(static_cast<int64_t>(str.size()), VariantDefs::kU32Size, |
| write_buffer_.data(), write_pos_); |
| write_pos_ += VariantDefs::kU32Size; |
| } else { |
| write_buffer_[write_pos_++] = |
| VariantBinaryUtil::ShortStrHeader(static_cast<int32_t>(str.size())); |
| } |
| memcpy(write_buffer_.data() + write_pos_, str.data(), str.size()); |
| write_pos_ += static_cast<int32_t>(str.size()); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendNull() { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kNull); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendBoolean(bool b) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1)); |
| write_buffer_[write_pos_++] = |
| VariantBinaryUtil::PrimitiveHeader(b ? VariantDefs::kTrue : VariantDefs::kFalse); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendLong(int64_t l) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8)); |
| if (l == static_cast<int8_t>(l)) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt1); |
| VariantBinaryUtil::WriteLong(l, 1, write_buffer_.data(), write_pos_); |
| write_pos_ += 1; |
| } else if (l == static_cast<int16_t>(l)) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt2); |
| VariantBinaryUtil::WriteLong(l, 2, write_buffer_.data(), write_pos_); |
| write_pos_ += 2; |
| } else if (l == static_cast<int32_t>(l)) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt4); |
| VariantBinaryUtil::WriteLong(l, 4, write_buffer_.data(), write_pos_); |
| write_pos_ += 4; |
| } else { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt8); |
| VariantBinaryUtil::WriteLong(l, 8, write_buffer_.data(), write_pos_); |
| write_pos_ += 8; |
| } |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendDouble(double d) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDouble); |
| int64_t bits; |
| memcpy(&bits, &d, sizeof(bits)); |
| VariantBinaryUtil::WriteLong(bits, 8, write_buffer_.data(), write_pos_); |
| write_pos_ += 8; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendDecimal(const VariantDecimal& d) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(2 + 16)); |
| int32_t precision = d.Precision(); |
| if (d.scale < 0 || d.scale > VariantDefs::kMaxDecimal16Precision || |
| precision > VariantDefs::kMaxDecimal16Precision) { |
| return Status::Invalid( |
| fmt::format("Decimal precision {} and scale {} must fit into the variant decimal " |
| "limit {}", |
| precision, d.scale, VariantDefs::kMaxDecimal16Precision)); |
| } |
| if (d.scale <= VariantDefs::kMaxDecimal4Precision && |
| precision <= VariantDefs::kMaxDecimal4Precision) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal4); |
| write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale); |
| VariantBinaryUtil::WriteLong(static_cast<int64_t>(d.unscaled), 4, write_buffer_.data(), |
| write_pos_); |
| write_pos_ += 4; |
| } else if (d.scale <= VariantDefs::kMaxDecimal8Precision && |
| precision <= VariantDefs::kMaxDecimal8Precision) { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal8); |
| write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale); |
| VariantBinaryUtil::WriteLong(static_cast<int64_t>(d.unscaled), 8, write_buffer_.data(), |
| write_pos_); |
| write_pos_ += 8; |
| } else { |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal16); |
| write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale); |
| auto bits = static_cast<__uint128_t>(d.unscaled); |
| for (int32_t i = 0; i < 16; ++i) { |
| write_buffer_[write_pos_ + i] = static_cast<uint8_t>((bits >> (8 * i)) & 0xFF); |
| } |
| write_pos_ += 16; |
| } |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendDate(int32_t days_since_epoch) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 4)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDate); |
| VariantBinaryUtil::WriteLong(days_since_epoch, 4, write_buffer_.data(), write_pos_); |
| write_pos_ += 4; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendTimestamp(int64_t micros_since_epoch) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kTimestamp); |
| VariantBinaryUtil::WriteLong(micros_since_epoch, 8, write_buffer_.data(), write_pos_); |
| write_pos_ += 8; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendTimestampNtz(int64_t micros_since_epoch) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kTimestampNtz); |
| VariantBinaryUtil::WriteLong(micros_since_epoch, 8, write_buffer_.data(), write_pos_); |
| write_pos_ += 8; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendFloat(float f) { |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 4)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kFloat); |
| int32_t bits; |
| memcpy(&bits, &f, sizeof(bits)); |
| VariantBinaryUtil::WriteLong(bits, 4, write_buffer_.data(), write_pos_); |
| write_pos_ += 4; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendBinary(std::string_view binary) { |
| PAIMON_RETURN_NOT_OK( |
| CheckCapacity(1 + VariantDefs::kU32Size + static_cast<int32_t>(binary.size()))); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kBinary); |
| VariantBinaryUtil::WriteLong(static_cast<int64_t>(binary.size()), VariantDefs::kU32Size, |
| write_buffer_.data(), write_pos_); |
| write_pos_ += VariantDefs::kU32Size; |
| memcpy(write_buffer_.data() + write_pos_, binary.data(), binary.size()); |
| write_pos_ += static_cast<int32_t>(binary.size()); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendUuid(std::string_view uuid_bytes) { |
| if (uuid_bytes.size() != 16) { |
| return Status::Invalid("UUID must be 16 bytes"); |
| } |
| PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 16)); |
| write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kUuid); |
| // UUID is stored big-endian, so don't use WriteLong. |
| memcpy(write_buffer_.data() + write_pos_, uuid_bytes.data(), 16); |
| write_pos_ += 16; |
| return Status::OK(); |
| } |
| |
| int32_t VariantBuilder::AddKey(std::string_view key) { |
| auto it = dictionary_.find(std::string(key)); |
| if (it != dictionary_.end()) { |
| return it->second; |
| } |
| auto id = static_cast<int32_t>(dictionary_keys_.size()); |
| dictionary_.emplace(std::string(key), id); |
| dictionary_keys_.emplace_back(key); |
| return id; |
| } |
| |
| Status VariantBuilder::FinishWritingObject(int32_t start, std::vector<FieldEntry>* fields) { |
| auto size = static_cast<int32_t>(fields->size()); |
| std::sort(fields->begin(), fields->end(), |
| [](const FieldEntry& a, const FieldEntry& b) { return a.key < b.key; }); |
| int32_t max_id = size == 0 ? 0 : (*fields)[0].id; |
| if (allow_duplicate_keys_) { |
| int32_t distinct_pos = 0; |
| // Maintain a list of distinct keys in-place. |
| for (int32_t i = 1; i < size; ++i) { |
| max_id = std::max(max_id, (*fields)[i].id); |
| if ((*fields)[i].id == (*fields)[i - 1].id) { |
| // Found a duplicate key. Keep the field with a greater offset. |
| if ((*fields)[distinct_pos].offset < (*fields)[i].offset) { |
| (*fields)[distinct_pos].offset = (*fields)[i].offset; |
| } |
| } else { |
| // Found a distinct key. Add the field to the list. |
| ++distinct_pos; |
| (*fields)[distinct_pos] = (*fields)[i]; |
| } |
| } |
| if (distinct_pos + 1 < size) { |
| size = distinct_pos + 1; |
| fields->erase(fields->begin() + size, fields->end()); |
| // Sort the fields by offsets so that we can move the value data of each field to the |
| // new offset without overwriting the fields after it. |
| std::sort(fields->begin(), fields->end(), |
| [](const FieldEntry& a, const FieldEntry& b) { return a.offset < b.offset; }); |
| int32_t current_offset = 0; |
| for (int32_t i = 0; i < size; ++i) { |
| int32_t old_offset = (*fields)[i].offset; |
| PAIMON_ASSIGN_OR_RAISE( |
| int32_t field_size, |
| VariantBinaryUtil::ValueSize( |
| std::string_view(reinterpret_cast<const char*>(write_buffer_.data()), |
| static_cast<size_t>(write_pos_)), |
| start + old_offset)); |
| memmove(write_buffer_.data() + start + current_offset, |
| write_buffer_.data() + start + old_offset, static_cast<size_t>(field_size)); |
| (*fields)[i].offset = current_offset; |
| current_offset += field_size; |
| } |
| write_pos_ = start + current_offset; |
| // Change back to the sort order by field keys to meet the variant spec. |
| std::sort(fields->begin(), fields->end(), |
| [](const FieldEntry& a, const FieldEntry& b) { return a.key < b.key; }); |
| } |
| } else { |
| for (int32_t i = 1; i < size; ++i) { |
| max_id = std::max(max_id, (*fields)[i].id); |
| if ((*fields)[i].key == (*fields)[i - 1].key) { |
| return Status::Invalid("VARIANT_DUPLICATE_KEY"); |
| } |
| } |
| } |
| int32_t data_size = write_pos_ - start; |
| bool large_size = size > VariantDefs::kU8Max; |
| int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1; |
| int32_t id_size = GetIntegerSize(max_id); |
| int32_t offset_size = GetIntegerSize(data_size); |
| // The space for the header byte, object size, id list, and offset list. |
| int32_t header_size = 1 + size_bytes + size * id_size + (size + 1) * offset_size; |
| PAIMON_RETURN_NOT_OK(CheckCapacity(header_size)); |
| // Shift the just-written field data to make room for the object header section. |
| memmove(write_buffer_.data() + start + header_size, write_buffer_.data() + start, |
| static_cast<size_t>(data_size)); |
| write_pos_ += header_size; |
| write_buffer_[start] = VariantBinaryUtil::ObjectHeader(large_size, id_size, offset_size); |
| VariantBinaryUtil::WriteLong(size, size_bytes, write_buffer_.data(), start + 1); |
| int32_t id_start = start + 1 + size_bytes; |
| int32_t offset_start = id_start + size * id_size; |
| for (int32_t i = 0; i < size; ++i) { |
| VariantBinaryUtil::WriteLong((*fields)[i].id, id_size, write_buffer_.data(), |
| id_start + i * id_size); |
| VariantBinaryUtil::WriteLong((*fields)[i].offset, offset_size, write_buffer_.data(), |
| offset_start + i * offset_size); |
| } |
| VariantBinaryUtil::WriteLong(data_size, offset_size, write_buffer_.data(), |
| offset_start + size * offset_size); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::FinishWritingArray(int32_t start, const std::vector<int32_t>& offsets) { |
| int32_t data_size = write_pos_ - start; |
| auto size = static_cast<int32_t>(offsets.size()); |
| bool large_size = size > VariantDefs::kU8Max; |
| int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1; |
| int32_t offset_size = GetIntegerSize(data_size); |
| // The space for the header byte, array size, and offset list. |
| int32_t header_size = 1 + size_bytes + (size + 1) * offset_size; |
| PAIMON_RETURN_NOT_OK(CheckCapacity(header_size)); |
| // Shift the just-written element data to make room for the header section. |
| memmove(write_buffer_.data() + start + header_size, write_buffer_.data() + start, |
| static_cast<size_t>(data_size)); |
| write_pos_ += header_size; |
| write_buffer_[start] = VariantBinaryUtil::ArrayHeader(large_size, offset_size); |
| VariantBinaryUtil::WriteLong(size, size_bytes, write_buffer_.data(), start + 1); |
| int32_t offset_start = start + 1 + size_bytes; |
| for (int32_t i = 0; i < size; ++i) { |
| VariantBinaryUtil::WriteLong(offsets[i], offset_size, write_buffer_.data(), |
| offset_start + i * offset_size); |
| } |
| VariantBinaryUtil::WriteLong(data_size, offset_size, write_buffer_.data(), |
| offset_start + size * offset_size); |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::AppendVariant(const GenericVariant& v) { |
| return AppendVariantImpl(v.RawValue(), v.Metadata(), v.Pos()); |
| } |
| |
| Status VariantBuilder::AppendVariantImpl(std::string_view value, std::string_view metadata, |
| int32_t pos) { |
| PAIMON_RETURN_NOT_OK(VariantBinaryUtil::CheckIndex(pos, static_cast<int32_t>(value.size()))); |
| int32_t basic_type = static_cast<uint8_t>(value[pos]) & VariantDefs::kBasicTypeMask; |
| switch (basic_type) { |
| case VariantDefs::kObject: { |
| PAIMON_ASSIGN_OR_RAISE(VariantBinaryUtil::ObjectInfo info, |
| VariantBinaryUtil::GetObjectInfo(value, pos)); |
| std::vector<FieldEntry> fields; |
| fields.reserve(info.num_elements); |
| int32_t start = write_pos_; |
| for (int32_t i = 0; i < info.num_elements; ++i) { |
| PAIMON_ASSIGN_OR_RAISE(int32_t id, |
| VariantBinaryUtil::ReadUnsigned( |
| value, info.id_start + info.id_size * i, info.id_size)); |
| PAIMON_ASSIGN_OR_RAISE( |
| int32_t offset, |
| VariantBinaryUtil::ReadUnsigned(value, info.offset_start + info.offset_size * i, |
| info.offset_size)); |
| int32_t element_pos = info.data_start + offset; |
| PAIMON_ASSIGN_OR_RAISE(std::string_view key, |
| VariantBinaryUtil::GetMetadataKey(metadata, id)); |
| int32_t new_id = AddKey(key); |
| fields.emplace_back(std::string(key), new_id, write_pos_ - start); |
| PAIMON_RETURN_NOT_OK(AppendVariantImpl(value, metadata, element_pos)); |
| } |
| return FinishWritingObject(start, &fields); |
| } |
| case VariantDefs::kArray: { |
| PAIMON_ASSIGN_OR_RAISE(VariantBinaryUtil::ArrayInfo info, |
| VariantBinaryUtil::GetArrayInfo(value, pos)); |
| std::vector<int32_t> offsets; |
| offsets.reserve(info.num_elements); |
| int32_t start = write_pos_; |
| for (int32_t i = 0; i < info.num_elements; ++i) { |
| PAIMON_ASSIGN_OR_RAISE( |
| int32_t offset, |
| VariantBinaryUtil::ReadUnsigned(value, info.offset_start + info.offset_size * i, |
| info.offset_size)); |
| int32_t element_pos = info.data_start + offset; |
| offsets.push_back(write_pos_ - start); |
| PAIMON_RETURN_NOT_OK(AppendVariantImpl(value, metadata, element_pos)); |
| } |
| return FinishWritingArray(start, offsets); |
| } |
| default: |
| return ShallowAppendVariant(value, pos); |
| } |
| } |
| |
| Status VariantBuilder::ShallowAppendVariant(std::string_view value, int32_t pos) { |
| PAIMON_ASSIGN_OR_RAISE(int32_t size, VariantBinaryUtil::ValueSize(value, pos)); |
| PAIMON_RETURN_NOT_OK( |
| VariantBinaryUtil::CheckIndex(pos + size - 1, static_cast<int32_t>(value.size()))); |
| PAIMON_RETURN_NOT_OK(CheckCapacity(size)); |
| memcpy(write_buffer_.data() + write_pos_, value.data() + pos, static_cast<size_t>(size)); |
| write_pos_ += size; |
| return Status::OK(); |
| } |
| |
| Status VariantBuilder::CheckCapacity(int32_t additional) { |
| int32_t required = write_pos_ + additional; |
| if (required > static_cast<int32_t>(write_buffer_.size())) { |
| // Allocate a new buffer with a capacity of the next power of 2 of `required`. |
| auto new_capacity = static_cast<int32_t>(write_buffer_.size()); |
| while (new_capacity < required) { |
| new_capacity *= 2; |
| if (new_capacity > VariantDefs::kSizeLimit) { |
| return Status::Invalid("VARIANT_SIZE_LIMIT"); |
| } |
| } |
| write_buffer_.resize(static_cast<size_t>(new_capacity)); |
| } |
| return Status::OK(); |
| } |
| |
| int32_t VariantBuilder::GetIntegerSize(int32_t value) { |
| if (value <= VariantDefs::kU8Max) { |
| return 1; |
| } |
| if (value <= VariantDefs::kU16Max) { |
| return 2; |
| } |
| if (value <= VariantDefs::kU24Max) { |
| return 3; |
| } |
| return 4; |
| } |
| |
| } // namespace paimon |