File type_object.hpp

File List > astutedds > xtypes > type_object.hpp

Go to the documentation of this file

//
// Copyright (c) 2026, Astute Systems PTY LTD
//
// This file is part of the Astute DDS developed by Astute Systems.
//
// See the commercial LICENSE file in the project root for full license details.
//
// @file type_object.hpp
// @brief DDS X-Types TypeObject and TypeIdentifier
//
// Implements the type system from DDS-XTypes 1.3 specification.
// Reference: https://www.omg.org/spec/DDS-XTypes/1.3/PDF
//

#ifndef ASTUTEDDS_XTYPES_TYPE_OBJECT_HPP
#define ASTUTEDDS_XTYPES_TYPE_OBJECT_HPP

#include <astutedds/cdr/cdr_types.hpp>

#include <array>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>

namespace astutedds::xtypes
{

// Type Kind (7.2.2.1)
enum class TypeKind : uint8_t
{
    TK_NONE = 0x00,
    TK_BOOLEAN = 0x01,
    TK_BYTE = 0x02,
    TK_INT16 = 0x03,
    TK_INT32 = 0x04,
    TK_INT64 = 0x05,
    TK_UINT16 = 0x06,
    TK_UINT32 = 0x07,
    TK_UINT64 = 0x08,
    TK_FLOAT32 = 0x09,
    TK_FLOAT64 = 0x0a,
    TK_FLOAT128 = 0x0b,
    TK_INT8 = 0x0c,
    TK_UINT8 = 0x0d,
    TK_CHAR8 = 0x10,
    TK_CHAR16 = 0x11,
    TK_STRING8 = 0x20,
    TK_STRING16 = 0x21,
    TK_ALIAS = 0x30,
    TK_ENUM = 0x31,
    TK_BITMASK = 0x32,
    TK_ANNOTATION = 0x33,
    TK_STRUCTURE = 0x34,
    TK_UNION = 0x35,
    TK_BITSET = 0x36,
    TK_SEQUENCE = 0x40,
    TK_ARRAY = 0x41,
    TK_MAP = 0x42
};

// Equivalence Hash (7.3.4.9)
using EquivalenceHash = std::array<uint8_t, 14>;

// Type Identifier discriminator (7.3.4.5.1)
enum class TypeIdKind : uint8_t
{
    TI_STRING8_SMALL = 0x70,
    TI_STRING8_LARGE = 0x71,
    TI_STRING16_SMALL = 0x72,
    TI_STRING16_LARGE = 0x73,
    TI_PLAIN_SEQUENCE_SMALL = 0x74,
    TI_PLAIN_SEQUENCE_LARGE = 0x75,
    TI_PLAIN_ARRAY_SMALL = 0x76,
    TI_PLAIN_ARRAY_LARGE = 0x77,
    TI_PLAIN_MAP_SMALL = 0x78,
    TI_PLAIN_MAP_LARGE = 0x79,
    TI_STRONGLY_CONNECTED_COMPONENT = 0x7a,
    EK_COMPLETE = 0x7b,
    EK_MINIMAL = 0x7c
};

// Forward declarations
struct TypeIdentifier;

// String Type Identifier (7.3.4.5.2)
struct StringSTypeDefn
{
    uint8_t bound{0};  // 0 = unbounded
};

struct StringLTypeDefn
{
    uint32_t bound{0};  // 0 = unbounded
};

// Sequence Type Identifier (7.3.4.5.3)
//
// element_identifier is a shared_ptr (rather than an owning raw pointer) so
// that the compiler-generated copy/move/destructor for this struct (and for
// the std::variant in TypeIdentifier that holds it) correctly manage the
// lifetime of the heap-allocated element type. A raw owning pointer here
// previously leaked on every copy/destruction since nothing ever called
// delete on it (see create_sequence_type_identifier()/create_array_type_identifier()).
struct PlainSequenceSElemDefn
{
    uint8_t bound{0};
    std::shared_ptr<TypeIdentifier> element_identifier;
};

struct PlainSequenceLElemDefn
{
    uint32_t bound{0};
    std::shared_ptr<TypeIdentifier> element_identifier;
};

// Array Type Identifier (7.3.4.5.4)
struct PlainArraySElemDefn
{
    std::vector<uint8_t> array_bound_seq;
    std::shared_ptr<TypeIdentifier> element_identifier;
};

struct PlainArrayLElemDefn
{
    std::vector<uint32_t> array_bound_seq;
    std::shared_ptr<TypeIdentifier> element_identifier;
};

// Map Type Identifier (7.3.4.5.5)
struct PlainMapSTypeDefn
{
    uint8_t bound{0};
    std::shared_ptr<TypeIdentifier> key_identifier;
    std::shared_ptr<TypeIdentifier> element_identifier;
};

struct PlainMapLTypeDefn
{
    uint32_t bound{0};
    std::shared_ptr<TypeIdentifier> key_identifier;
    std::shared_ptr<TypeIdentifier> element_identifier;
};

// Strongly-Connected Component identifier (7.3.4.9.3) — identifies the set of
// mutually-dependent types (a "SCC") that a recursive/cyclic TypeIdentifier
// belongs to. `sc_component_id` is the EquivalenceHash of the serialized
// TypeObjectSeq for the whole component (discriminator is always EK_MINIMAL or
// EK_COMPLETE); `scc_length` is the number of types in the component.
struct StronglyConnectedComponentId
{
    TypeIdKind discriminator{TypeIdKind::EK_MINIMAL};
    EquivalenceHash sc_component_id{};
    uint32_t scc_length{0};
};

// Strongly-Connected Component TypeIdentifier payload (7.3.4.6.4) — identifies
// one specific type (`scc_index`, 1-based) within a strongly-connected
// component. An `scc_index` of 0 denotes the abstract SCCIdentifier itself
// (a reference to the whole component rather than one member type).
struct SCCTypeIdentifierDefn
{
    StronglyConnectedComponentId component_id;
    uint32_t scc_index{0};
};

// Type Identifier (7.3.4.5)
struct TypeIdentifier
{
    using VariantType =
        std::variant<std::monostate,  // TK_NONE or primitive types
                     StringSTypeDefn, StringLTypeDefn, PlainSequenceSElemDefn, PlainSequenceLElemDefn,
                     PlainArraySElemDefn, PlainArrayLElemDefn, PlainMapSTypeDefn, PlainMapLTypeDefn, EquivalenceHash,
                     SCCTypeIdentifierDefn>;

    TypeIdKind discriminator{TypeIdKind::EK_MINIMAL};
    VariantType value;
};

// Member Flags (7.3.1.2.1.4)
struct MemberFlag
{
    static constexpr uint16_t TRY_CONSTRUCT_DISCARD = 0x0001;
    static constexpr uint16_t TRY_CONSTRUCT_USE_DEFAULT = 0x0002;
    static constexpr uint16_t IS_EXTERNAL = 0x0004;
    static constexpr uint16_t IS_OPTIONAL = 0x0008;
    static constexpr uint16_t IS_MUST_UNDERSTAND = 0x0010;
    static constexpr uint16_t IS_KEY = 0x0020;
    static constexpr uint16_t IS_DEFAULT = 0x0040;
};

// Common Structure Member (7.3.1.2.1)
struct CommonStructMember
{
    uint32_t member_id{0};
    uint16_t member_flags{0};
    TypeIdentifier member_type_id;
};

// Complete Structure Member (7.3.1.2.2)
struct CompleteStructMember
{
    CommonStructMember common;
    std::string name;
    std::vector<std::string> annotations;  // Simplified
};

// Minimal Structure Member (7.3.1.2.3)
struct MinimalStructMember
{
    CommonStructMember common;
    uint32_t name_hash{0};  // Hash of the member name
};

// Structure Type (7.3.1.2.4)
//
// `base_type` records single-inheritance aggregate types (§7.2.2.4.5, "struct
// Derived : Base { ... }"). Unset (std::nullopt) means the struct has no base
// type, matching the spec's "+base_type 0..1" optional association.
struct CompleteStructType
{
    cdr::ExtensibilityKind extensibility{cdr::ExtensibilityKind::FINAL};
    std::vector<CompleteStructMember> members;
    std::string name;
    std::optional<TypeIdentifier> base_type;
};

struct MinimalStructType
{
    cdr::ExtensibilityKind extensibility{cdr::ExtensibilityKind::FINAL};
    std::vector<MinimalStructMember> members;
    std::optional<TypeIdentifier> base_type;
};

// Union Case Label Sequence (7.3.1.2.1) — discriminator values selecting a member.
// An empty sequence combined with MemberFlag::IS_DEFAULT denotes the default case.
using UnionCaseLabelSeq = std::vector<int32_t>;

// Common Union Member (7.3.1.2.1)
struct CommonUnionMember
{
    uint32_t member_id{0};
    uint16_t member_flags{0};
    TypeIdentifier member_type_id;
    UnionCaseLabelSeq label_seq;
};

// Complete Union Member (7.3.1.2.2)
struct CompleteUnionMember
{
    CommonUnionMember common;
    std::string name;
    std::vector<std::string> annotations;  // Simplified
};

// Minimal Union Member (7.3.1.2.3)
struct MinimalUnionMember
{
    CommonUnionMember common;
    uint32_t name_hash{0};  // Hash of the member name
};

// Common Discriminator Member (7.3.1.2.1) — the union's `switch` type and its flags.
// IS_MUST_UNDERSTAND is implicit for discriminators per the spec; IS_KEY and the
// TRY_CONSTRUCT_* bits are the only flags that apply here.
struct CommonDiscriminatorMember
{
    uint16_t member_flags{0};
    TypeIdentifier type_id;
};

// Union Type (7.3.1.2.4)
struct CompleteUnionType
{
    cdr::ExtensibilityKind extensibility{cdr::ExtensibilityKind::FINAL};
    CommonDiscriminatorMember discriminator;
    std::vector<CompleteUnionMember> members;
    std::string name;
};

struct MinimalUnionType
{
    cdr::ExtensibilityKind extensibility{cdr::ExtensibilityKind::FINAL};
    CommonDiscriminatorMember discriminator;
    std::vector<MinimalUnionMember> members;
};

// Enum Literal (7.3.1.3.1)
struct CommonEnumeratedLiteral
{
    int32_t value{0};
    uint16_t flags{0};
};

struct CompleteEnumeratedLiteral
{
    CommonEnumeratedLiteral common;
    std::string name;
};

struct MinimalEnumeratedLiteral
{
    CommonEnumeratedLiteral common;
    uint32_t name_hash{0};
};

// Enum Type (7.3.1.3.2)
struct CompleteEnumeratedType
{
    uint16_t bit_bound{32};  // Number of bits used
    std::vector<CompleteEnumeratedLiteral> literals;
    std::string name;
};

struct MinimalEnumeratedType
{
    uint16_t bit_bound{32};
    std::vector<MinimalEnumeratedLiteral> literals;
};

// Common Bitflag (7.3.1.4.1) — a single named bit position within a bitmask.
struct CommonBitflag
{
    uint16_t position{0};
    uint16_t flags{0};
};

// Complete Bitflag (7.3.1.4.2)
struct CompleteBitflag
{
    CommonBitflag common;
    std::string name;
    std::vector<std::string> annotations;  // Simplified
};

// Minimal Bitflag (7.3.1.4.3)
struct MinimalBitflag
{
    CommonBitflag common;
    uint32_t name_hash{0};  // Hash of the flag name
};

// Bitmask Type (7.3.1.4.4) — ordered by CommonBitflag::position per spec.
// `bit_bound` selects the underlying storage width (8/16/32/64), driven by the
// IDL `@bit_bound(N)` annotation (defaults to 32 when unspecified, matching the
// enum convention above).
struct CompleteBitmaskType
{
    uint16_t bit_bound{32};
    std::vector<CompleteBitflag> flags;
    std::string name;
};

struct MinimalBitmaskType
{
    uint16_t bit_bound{32};
    std::vector<MinimalBitflag> flags;
};

// Alias Type (7.2.2.4.2, TK_ALIAS) — represents an IDL `typedef` as a distinct
// TypeObject rather than collapsing it into the underlying type at parse time,
// so that typedef-heavy IDL keeps a stable TypeIdentifier hash and can be
// checked for assignability against the aliased type (§7.2.4.4.3).
struct CompleteAliasType
{
    TypeIdentifier related_type;
    std::string name;
};

struct MinimalAliasType
{
    TypeIdentifier related_type;
};

// Type Object (7.3.4.2)
struct CompleteTypeObject
{
    using VariantType = std::variant<std::monostate, CompleteStructType, CompleteEnumeratedType, CompleteUnionType,
                                     CompleteBitmaskType, CompleteAliasType
                                     // Add other types as needed
                                     >;

    VariantType value;
};

struct MinimalTypeObject
{
    using VariantType = std::variant<std::monostate, MinimalStructType, MinimalEnumeratedType, MinimalUnionType,
                                     MinimalBitmaskType, MinimalAliasType
                                     // Add other types as needed
                                     >;

    VariantType value;
};

struct TypeObject
{
    using VariantType = std::variant<CompleteTypeObject, MinimalTypeObject>;

    VariantType value;
};

// Factory functions for creating TypeObjects

CompleteTypeObject create_complete_struct_type_object(
    const std::string& name, const std::vector<CompleteStructMember>& members,
    cdr::ExtensibilityKind extensibility = cdr::ExtensibilityKind::FINAL,
    const std::optional<TypeIdentifier>& base_type = std::nullopt);

MinimalTypeObject create_minimal_struct_type_object(
    const std::vector<MinimalStructMember>& members,
    cdr::ExtensibilityKind extensibility = cdr::ExtensibilityKind::FINAL,
    const std::optional<TypeIdentifier>& base_type = std::nullopt);

CompleteTypeObject create_complete_enum_type_object(const std::string& name,
                                                    const std::vector<CompleteEnumeratedLiteral>& literals,
                                                    uint16_t bit_bound = 32);

MinimalTypeObject create_minimal_enum_type_object(const std::vector<MinimalEnumeratedLiteral>& literals,
                                                  uint16_t bit_bound = 32);

CompleteTypeObject create_complete_union_type_object(
    const std::string& name, const TypeIdentifier& discriminator_type,
    const std::vector<CompleteUnionMember>& members,
    cdr::ExtensibilityKind extensibility = cdr::ExtensibilityKind::FINAL, uint16_t discriminator_flags = 0);

MinimalTypeObject create_minimal_union_type_object(const TypeIdentifier& discriminator_type,
                                                   const std::vector<MinimalUnionMember>& members,
                                                   cdr::ExtensibilityKind extensibility = cdr::ExtensibilityKind::FINAL,
                                                   uint16_t discriminator_flags = 0);

CompleteTypeObject create_complete_bitmask_type_object(const std::string& name,
                                                       const std::vector<CompleteBitflag>& flags,
                                                       uint16_t bit_bound = 32);

MinimalTypeObject create_minimal_bitmask_type_object(const std::vector<MinimalBitflag>& flags,
                                                     uint16_t bit_bound = 32);

CompleteTypeObject create_complete_alias_type_object(const std::string& name, const TypeIdentifier& related_type);

MinimalTypeObject create_minimal_alias_type_object(const TypeIdentifier& related_type);

// TypeIdentifier creation functions

TypeIdentifier create_primitive_type_identifier(TypeKind kind);

TypeIdentifier create_string_type_identifier(uint32_t bound = 0);

TypeIdentifier create_sequence_type_identifier(const TypeIdentifier& element_type, uint32_t bound = 0);

TypeIdentifier create_array_type_identifier(const TypeIdentifier& element_type, const std::vector<uint32_t>& bounds);

TypeIdentifier create_map_type_identifier(const TypeIdentifier& key_type, const TypeIdentifier& element_type,
                                          uint32_t bound = 0);

TypeIdentifier create_scc_type_identifier(const StronglyConnectedComponentId& component_id, uint32_t scc_index);

std::vector<uint8_t> serialize_type_object(const TypeObject& type_obj);

EquivalenceHash generate_equivalence_hash(const TypeObject& type_obj);

TypeIdentifier create_type_identifier_from_hash(const EquivalenceHash& hash, bool use_minimal = true);

MinimalStructMember to_minimal_struct_member(const CompleteStructMember& complete);

MinimalEnumeratedLiteral to_minimal_enum_literal(const CompleteEnumeratedLiteral& complete);

MinimalUnionMember to_minimal_union_member(const CompleteUnionMember& complete);

MinimalBitflag to_minimal_bitflag(const CompleteBitflag& complete);

// Assignability functions (DDS-XTypes 1.3 Section 7.6.3)

struct TypeConsistencyOptions
{
    bool allow_type_widening{false};

    bool ignore_sequence_bounds{false};

    bool ignore_string_bounds{false};
};

bool is_primitive_assignable(TypeKind source, TypeKind target);

bool is_primitive_assignable(TypeKind source, TypeKind target, const TypeConsistencyOptions& options);

bool is_string_assignable(const TypeIdentifier& source, const TypeIdentifier& target);

bool is_string_assignable(const TypeIdentifier& source, const TypeIdentifier& target,
                          const TypeConsistencyOptions& options);

bool is_sequence_assignable(const TypeIdentifier& source, const TypeIdentifier& target);
bool is_sequence_assignable(const TypeIdentifier& source, const TypeIdentifier& target,
                            const TypeConsistencyOptions& options);

bool is_array_assignable(const TypeIdentifier& source, const TypeIdentifier& target);
bool is_array_assignable(const TypeIdentifier& source, const TypeIdentifier& target,
                         const TypeConsistencyOptions& options);

bool is_map_assignable(const TypeIdentifier& source, const TypeIdentifier& target);
bool is_map_assignable(const TypeIdentifier& source, const TypeIdentifier& target,
                       const TypeConsistencyOptions& options);

bool is_type_identifier_assignable(const TypeIdentifier& source, const TypeIdentifier& target);
bool is_type_identifier_assignable(const TypeIdentifier& source, const TypeIdentifier& target,
                                   const TypeConsistencyOptions& options);

bool are_struct_members_assignable(const CommonStructMember& source, const CommonStructMember& target);

bool is_struct_assignable(const CompleteStructType& source, const CompleteStructType& target);

bool is_enum_assignable(const CompleteEnumeratedType& source, const CompleteEnumeratedType& target);

bool is_union_assignable(const CompleteUnionType& source, const CompleteUnionType& target);
bool is_union_assignable(const CompleteUnionType& source, const CompleteUnionType& target,
                         const TypeConsistencyOptions& options);

bool is_bitmask_assignable(const CompleteBitmaskType& source, const CompleteBitmaskType& target);

bool is_alias_assignable(const CompleteAliasType& source, const CompleteAliasType& target);
bool is_alias_assignable(const CompleteAliasType& source, const CompleteAliasType& target,
                         const TypeConsistencyOptions& options);

bool is_scc_type_identifier_assignable(const SCCTypeIdentifierDefn& source, const SCCTypeIdentifierDefn& target);

bool is_assignable(const TypeObject& source, const TypeObject& target);
bool is_assignable(const TypeObject& source, const TypeObject& target, const TypeConsistencyOptions& options);

// TypeIdentifier helper functions

bool operator==(const TypeIdentifier& lhs, const TypeIdentifier& rhs);
bool operator!=(const TypeIdentifier& lhs, const TypeIdentifier& rhs);

std::string to_string(const TypeIdentifier& ti);

TypeIdentifier clone_type_identifier(const TypeIdentifier& source);

bool is_primitive_type_identifier(const TypeIdentifier& ti);

bool is_string_type_identifier(const TypeIdentifier& ti);

bool is_collection_type_identifier(const TypeIdentifier& ti);

}  // namespace astutedds::xtypes

#endif  // ASTUTEDDS_XTYPES_TYPE_OBJECT_HPP