blob: 5261a14d9fa135f0001f412b46b8ec9ec6bba096 [file] [log] [blame]
/*
* 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.
*/
#pragma once
#include "fory/serialization/serializer.h"
#include "fory/type/type.h"
#include "fory/util/error.h"
#include <cstdint>
#include <variant>
namespace fory {
namespace serialization {
// ============================================================================
// std::monostate Serializer
// ============================================================================
/// Serializer for std::monostate (empty variant alternative)
///
/// std::monostate represents an empty state in variants. It has no data
/// to serialize, so serialization is a no-op.
template <> struct Serializer<std::monostate> {
static constexpr TypeId type_id =
TypeId::NONE; // Use NONE for empty/not-applicable type
static inline void write_type_info(WriteContext &ctx) {
ctx.write_varuint32(static_cast<uint32_t>(type_id));
}
static inline void read_type_info(ReadContext &ctx) {
// Read and validate type_id for monostate
ctx.read_varuint32(ctx.error());
// Accept any type since monostate is just a marker with no data
}
static inline void write(const std::monostate &, WriteContext &ctx,
RefMode ref_mode, bool write_type,
bool has_generics = false) {
(void)has_generics;
write_not_null_ref_flag(ctx, ref_mode);
if (write_type) {
write_type_info(ctx);
}
// No data to write for monostate
}
static inline void write_data(const std::monostate &, WriteContext &ctx) {
// No data to write for monostate
}
static inline void write_data_generic(const std::monostate &,
WriteContext &ctx, bool has_generics) {
// No data to write for monostate
}
static inline std::monostate read(ReadContext &ctx, RefMode ref_mode,
bool read_type) {
bool has_value = read_null_only_flag(ctx, ref_mode);
if (!has_value) {
return std::monostate{};
}
if (read_type) {
read_type_info(ctx);
}
return std::monostate{};
}
static inline std::monostate read_data(ReadContext &ctx) {
// No data to read for monostate
return std::monostate{};
}
static inline std::monostate read_with_type_info(ReadContext &ctx,
RefMode ref_mode,
const TypeInfo &type_info) {
return read(ctx, ref_mode, false);
}
};
// ============================================================================
// std::variant Serialization Helpers
// ============================================================================
/// Helper to write variant data by dispatching to the correct alternative's
/// serializer based on the active index.
template <typename Variant, size_t Index = 0>
inline void write_variant_by_index(const Variant &variant, WriteContext &ctx,
size_t active_index, RefMode ref_mode,
bool write_type) {
if constexpr (Index < std::variant_size_v<Variant>) {
if (Index == active_index) {
using AlternativeType = std::variant_alternative_t<Index, Variant>;
const auto &value = std::get<Index>(variant);
Serializer<AlternativeType>::write(value, ctx, ref_mode, write_type);
} else {
write_variant_by_index<Variant, Index + 1>(variant, ctx, active_index,
ref_mode, write_type);
}
} else {
// Should never reach here if active_index is valid
ctx.set_error(Error::invalid_data("Invalid variant index: " +
std::to_string(active_index)));
}
}
/// Helper to read variant data by dispatching to the correct alternative's
/// serializer based on the stored index.
template <typename Variant, size_t Index = 0>
inline Variant read_variant_by_index(ReadContext &ctx, size_t stored_index,
RefMode ref_mode, bool read_type) {
if constexpr (Index < std::variant_size_v<Variant>) {
if (Index == stored_index) {
using AlternativeType = std::variant_alternative_t<Index, Variant>;
AlternativeType value =
Serializer<AlternativeType>::read(ctx, ref_mode, read_type);
if (FORY_PREDICT_FALSE(ctx.has_error())) {
// Return a default-constructed variant with the first alternative
return Variant{};
}
return Variant{std::in_place_index<Index>, std::move(value)};
} else {
return read_variant_by_index<Variant, Index + 1>(ctx, stored_index,
ref_mode, read_type);
}
} else {
// Invalid index - set error and return default
ctx.set_error(Error::invalid_data("Invalid variant index during read: " +
std::to_string(stored_index)));
return Variant{};
}
}
// ============================================================================
// std::variant Serializer
// ============================================================================
/// Serializer for std::variant<Ts...>
///
/// Serializes variant by storing:
/// 1. The active alternative index (as varuint32)
/// 2. The value of the active alternative
///
/// This allows the deserializer to determine which alternative to construct
/// and forward to the appropriate serializer.
///
/// Example:
/// ```cpp
/// std::variant<int, std::string, double> v = "hello";
/// // Serializes: index=1, then string data
/// ```
template <typename... Ts> struct Serializer<std::variant<Ts...>> {
// Use UNION type_id since variant is a sum type (union) that can hold
// one of several alternative types. The actual alternative type is determined
// by the stored index.
static constexpr TypeId type_id = TypeId::UNION;
using VariantType = std::variant<Ts...>;
static inline void write_type_info(WriteContext &ctx) {
// Write UNION type_id to indicate variant/sum type
ctx.write_varuint32(static_cast<uint32_t>(type_id));
}
static inline void read_type_info(ReadContext &ctx) {
// Read and validate UNION type_id
uint32_t type_id_read = ctx.read_varuint32(ctx.error());
if (FORY_PREDICT_FALSE(ctx.has_error())) {
return;
}
if (!type_id_matches(type_id_read, static_cast<uint32_t>(type_id))) {
ctx.set_error(
Error::type_mismatch(type_id_read, static_cast<uint32_t>(type_id)));
}
}
static inline void write(const VariantType &variant, WriteContext &ctx,
RefMode ref_mode, bool write_type,
bool has_generics = false) {
(void)has_generics;
write_not_null_ref_flag(ctx, ref_mode);
if (write_type) {
write_type_info(ctx);
}
write_data(variant, ctx);
}
static inline void write_data(const VariantType &variant, WriteContext &ctx) {
// Write the active variant index
size_t active_index = variant.index();
ctx.write_varuint32(static_cast<uint32_t>(active_index));
// Dispatch to the appropriate alternative's serializer
// In xlang/compatible mode, write type info for the alternative
bool write_alt_type = ctx.is_xlang() || ctx.is_compatible();
RefMode alt_ref_mode = ctx.is_xlang() ? RefMode::Tracking : RefMode::None;
write_variant_by_index(variant, ctx, active_index, alt_ref_mode,
write_alt_type);
}
static inline void write_data_generic(const VariantType &variant,
WriteContext &ctx, bool has_generics) {
write_data(variant, ctx);
}
static inline VariantType read(ReadContext &ctx, RefMode ref_mode,
bool read_type) {
bool has_value = read_null_only_flag(ctx, ref_mode);
if (!has_value) {
return VariantType{};
}
if (read_type) {
read_type_info(ctx);
}
return read_data(ctx);
}
static inline VariantType read_data(ReadContext &ctx) {
// Read the stored variant index
uint32_t stored_index = ctx.read_varuint32(ctx.error());
// Validate index is within bounds
if (FORY_PREDICT_FALSE(stored_index >= std::variant_size_v<VariantType>)) {
ctx.set_error(Error::invalid_data(
"Variant index out of bounds: " + std::to_string(stored_index) +
" (max: " + std::to_string(std::variant_size_v<VariantType> - 1) +
")"));
return VariantType{};
}
// Dispatch to the appropriate alternative's serializer
// In xlang/compatible mode, read type info for the alternative
bool read_alt_type = ctx.is_xlang() || ctx.is_compatible();
RefMode alt_ref_mode = ctx.is_xlang() ? RefMode::Tracking : RefMode::None;
return read_variant_by_index<VariantType>(ctx, stored_index, alt_ref_mode,
read_alt_type);
}
static inline VariantType read_with_type_info(ReadContext &ctx,
RefMode ref_mode,
const TypeInfo &type_info) {
return read(ctx, ref_mode, false);
}
};
} // namespace serialization
} // namespace fory