This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new c11d5899e perf(c++): remove shared_ptr from type info to reduce atomic 
counter cost (#2951)
c11d5899e is described below

commit c11d5899e0fa211478f65f74ee473e34d2a14b29
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Nov 30 22:46:02 2025 +0800

    perf(c++): remove shared_ptr from type info to reduce atomic counter cost 
(#2951)
    
    ## Why?
    
    Eliminate `shared_ptr<TypeInfo>` overhead from the hot serialization
    code path. The atomic reference counting in `shared_ptr` adds
    unnecessary overhead when TypeInfo objects are accessed frequently
    during serialization/deserialization. By switching to raw pointers with
    clear ownership semantics, we can improve performance on the critical
    path.
    
    ## What does this PR do?
    
    This PR refactors the C++ serialization library's TypeInfo ownership
    model:
    
    ### TypeInfo Changes
    - Changed internal fields (`type_meta`, `encoded_namespace`,
    `encoded_type_name`) from `shared_ptr` to `unique_ptr`
    - Added `TypeInfo::deep_clone()` method for creating deep copies
    - Made `TypeInfo` non-copyable (deleted copy constructor/assignment)
    
    ### TypeResolver Storage Changes
    - Changed from `map<K, shared_ptr<TypeInfo>>` to primary storage
    pattern:
    - Primary storage: `vector<unique_ptr<TypeInfo>>` (owns all TypeInfo
    objects)
      - Lookup maps: Raw pointers (`TypeInfo*`) pointing to primary storage
    - Lookup methods now return:
    - `const TypeInfo*` for nullable lookups (`get_type_info_by_id`,
    `get_type_info_by_name`)
    - `Result<const TypeInfo&>` for error-handling lookups (`get_type_info`,
    `get_struct_type_info<T>`)
    
    ### Context Ownership Changes
    - `WriteContext` and `ReadContext` now hold `unique_ptr<TypeResolver>`
    instead of `shared_ptr`
    - Contexts are created lazily after type resolver finalization with
    deep-cloned resolvers
    - `Fory` class uses `std::optional` for contexts to support lazy
    initialization
    
    ### Code Pattern Updates
    - Updated all call sites that used `FORY_TRY` with `Result<const
    TypeInfo&>` to use explicit error handling (since TypeInfo is now
    non-copyable)
    - Updated `read_struct_fields_compatible` signature from
    `shared_ptr<TypeMeta>` to `const TypeMeta*`
    
    ## Related issues
    
    #2906
    #2944
    
    ## Does this PR introduce any user-facing change?
    
    - [x] Does this PR introduce any public API change?
    - Internal API only. TypeResolver lookup methods now return raw pointers
    or references instead of shared_ptr. User-facing Fory API remains
    unchanged.
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    This change eliminates atomic reference counting overhead on the hot
    serialization path:
    - `shared_ptr` copy/destruction involves atomic increment/decrement
    operations
    - Raw pointer access is a simple dereference with no atomic operations
    - Deep cloning happens once during context creation, not
    per-serialization
    
    Expected improvement: Reduced CPU overhead in tight serialization loops,
    especially noticeable when serializing many small objects where TypeInfo
    lookups are frequent relative to data size.
---
 cpp/fory/serialization/context.cc            | 121 ++++---------
 cpp/fory/serialization/context.h             |  35 ++--
 cpp/fory/serialization/fory.h                |  57 +++---
 cpp/fory/serialization/map_serializer.h      |  28 +--
 cpp/fory/serialization/serialization_test.cc |  56 ++++--
 cpp/fory/serialization/skip.cc               |   6 +-
 cpp/fory/serialization/struct_serializer.h   |  47 ++---
 cpp/fory/serialization/type_info.h           |  20 ++-
 cpp/fory/serialization/type_resolver.cc      | 257 ++++++++++++++++-----------
 cpp/fory/serialization/type_resolver.h       | 252 ++++++++++----------------
 10 files changed, 427 insertions(+), 452 deletions(-)

diff --git a/cpp/fory/serialization/context.cc 
b/cpp/fory/serialization/context.cc
index 8099ff64c..9a43001ee 100644
--- a/cpp/fory/serialization/context.cc
+++ b/cpp/fory/serialization/context.cc
@@ -51,51 +51,14 @@ static const std::vector<MetaEncoding> kTypeNameEncodings = 
{
     MetaEncoding::LOWER_UPPER_DIGIT_SPECIAL,
     MetaEncoding::FIRST_TO_LOWER_SPECIAL};
 
-// ============================================================================
-// encode_meta_string - Pre-encode meta strings during registration
-// ============================================================================
-
-Result<std::shared_ptr<CachedMetaString>, Error>
-encode_meta_string(const std::string &value, bool is_namespace) {
-  auto result = std::make_shared<CachedMetaString>();
-  result->original = value;
-
-  if (value.empty()) {
-    result->bytes.clear();
-    result->encoding = static_cast<uint8_t>(MetaEncoding::UTF8);
-    result->hash = 0;
-    return result;
-  }
-
-  // Choose encoder and encodings based on whether this is namespace or type
-  // name
-  const auto &encoder = is_namespace ? kNamespaceEncoder : kTypeNameEncoder;
-  const auto &encodings = is_namespace ? kPkgEncodings : kTypeNameEncodings;
-
-  // Encode the string
-  FORY_TRY(encoded, encoder.encode(value, encodings));
-  result->bytes = std::move(encoded.bytes);
-  result->encoding = static_cast<uint8_t>(encoded.encoding);
-
-  // Pre-compute hash for large strings (>16 bytes)
-  if (result->bytes.size() > kSmallStringThreshold) {
-    int64_t hash_out[2] = {0, 0};
-    MurmurHash3_x64_128(result->bytes.data(),
-                        static_cast<int>(result->bytes.size()), 47, hash_out);
-    result->hash = hash_out[0];
-  } else {
-    result->hash = 0;
-  }
-
-  return result;
-}
+// Note: encode_meta_string is now implemented in type_resolver.cc
 
 // ============================================================================
 // WriteContext Implementation
 // ============================================================================
 
 WriteContext::WriteContext(const Config &config,
-                           std::shared_ptr<TypeResolver> type_resolver)
+                           std::unique_ptr<TypeResolver> type_resolver)
     : buffer_(), config_(&config), type_resolver_(std::move(type_resolver)),
       current_dyn_depth_(0) {}
 
@@ -163,14 +126,7 @@ static void write_encoded_meta_string(Buffer &buffer,
 
 Result<void, Error>
 WriteContext::write_enum_typeinfo(const std::type_index &type) {
-  auto type_info_result = type_resolver_->get_type_info(type);
-  if (!type_info_result.ok()) {
-    // Enum not registered, write plain ENUM type id
-    buffer_.WriteVarUint32(static_cast<uint32_t>(TypeId::ENUM));
-    return Result<void, Error>();
-  }
-
-  const auto &type_info = type_info_result.value();
+  FORY_TRY(type_info, type_resolver_->get_type_info(type));
   uint32_t type_id = type_info->type_id;
   uint32_t type_id_low = type_id & 0xff;
 
@@ -200,9 +156,7 @@ WriteContext::write_enum_typeinfo(const std::type_index 
&type) {
 Result<void, Error>
 WriteContext::write_enum_typeinfo(const TypeInfo *type_info) {
   if (!type_info) {
-    // Enum not registered, write plain ENUM type id
-    buffer_.WriteVarUint32(static_cast<uint32_t>(TypeId::ENUM));
-    return Result<void, Error>();
+    return Unexpected(Error::type_error("Enum type not registered"));
   }
 
   uint32_t type_id = type_info->type_id;
@@ -237,12 +191,8 @@ WriteContext::write_any_typeinfo(uint32_t fory_type_id,
   // Check if it's an internal type
   if (is_internal_type(fory_type_id)) {
     buffer_.WriteVarUint32(fory_type_id);
-    auto type_info = type_resolver_->get_type_info_by_id(fory_type_id);
-    if (!type_info) {
-      return Unexpected(
-          Error::type_error("Type info for internal type not found"));
-    }
-    return type_info.get();
+    FORY_TRY(type_info, type_resolver_->get_type_info_by_id(fory_type_id));
+    return type_info;
   }
 
   // Get type info for the concrete type
@@ -401,7 +351,7 @@ uint32_t WriteContext::get_type_id_for_cache(const 
std::type_index &type_idx) {
 // ============================================================================
 
 ReadContext::ReadContext(const Config &config,
-                         std::shared_ptr<TypeResolver> type_resolver)
+                         std::unique_ptr<TypeResolver> type_resolver)
     : buffer_(nullptr), config_(&config),
       type_resolver_(std::move(type_resolver)), current_dyn_depth_(0) {}
 
@@ -411,14 +361,14 @@ ReadContext::~ReadContext() = default;
 static const MetaStringDecoder kNamespaceDecoder('.', '_');
 static const MetaStringDecoder kTypeNameDecoder('$', '_');
 
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<const TypeInfo *, Error>
 ReadContext::read_enum_type_info(const std::type_index &type,
                                  uint32_t base_type_id) {
   (void)type;
   return read_enum_type_info(base_type_id);
 }
 
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<const TypeInfo *, Error>
 ReadContext::read_enum_type_info(uint32_t base_type_id) {
   FORY_TRY(type_info, read_any_typeinfo());
   uint32_t type_id_low = type_info->type_id & 0xff;
@@ -459,18 +409,24 @@ Result<size_t, Error> ReadContext::load_type_meta(int32_t 
meta_offset) {
     FORY_TRY(parsed_meta,
              TypeMeta::from_bytes_with_header(*buffer_, meta_header));
 
-    // Find local TypeInfo to get field_id mapping
-    std::shared_ptr<TypeInfo> local_type_info = nullptr;
+    // Find local TypeInfo to get field_id mapping (optional for schema
+    // evolution)
+    const TypeInfo *local_type_info = nullptr;
     if (parsed_meta->register_by_name) {
-      local_type_info = type_resolver_->get_type_info_by_name(
+      auto result = type_resolver_->get_type_info_by_name(
           parsed_meta->namespace_str, parsed_meta->type_name);
+      if (result.ok()) {
+        local_type_info = result.value();
+      }
     } else {
-      local_type_info =
-          type_resolver_->get_type_info_by_id(parsed_meta->type_id);
+      auto result = type_resolver_->get_type_info_by_id(parsed_meta->type_id);
+      if (result.ok()) {
+        local_type_info = result.value();
+      }
     }
 
     // Create TypeInfo with field_ids assigned
-    std::shared_ptr<TypeInfo> type_info;
+    auto type_info = std::make_unique<TypeInfo>();
     if (local_type_info) {
       // Have local type - assign field_ids by comparing schemas
       // Note: Extension types don't have type_meta (only structs do)
@@ -478,9 +434,8 @@ Result<size_t, Error> ReadContext::load_type_meta(int32_t 
meta_offset) {
         TypeMeta::assign_field_ids(local_type_info->type_meta.get(),
                                    parsed_meta->field_infos);
       }
-      type_info = std::make_shared<TypeInfo>();
       type_info->type_id = local_type_info->type_id;
-      type_info->type_meta = parsed_meta;
+      type_info->type_meta = std::move(parsed_meta);
       type_info->type_def = local_type_info->type_def;
       // CRITICAL: Copy the harness from the registered type_info
       type_info->harness = local_type_info->harness;
@@ -490,17 +445,22 @@ Result<size_t, Error> ReadContext::load_type_meta(int32_t 
meta_offset) {
       type_info->register_by_name = local_type_info->register_by_name;
     } else {
       // No local type - create stub TypeInfo with parsed meta
-      type_info = std::make_shared<TypeInfo>();
       type_info->type_id = parsed_meta->type_id;
-      type_info->type_meta = parsed_meta;
+      type_info->type_meta = std::move(parsed_meta);
     }
 
+    // Get raw pointer before moving into storage
+    const TypeInfo *raw_ptr = type_info.get();
+
+    // Store in primary storage
+    owned_reading_type_infos_.push_back(std::move(type_info));
+
     // Cache the parsed TypeInfo (with size limit to prevent OOM)
     if (parsed_type_infos_.size() < kMaxParsedNumTypeDefs) {
-      parsed_type_infos_[meta_header] = type_info;
+      parsed_type_infos_[meta_header] = raw_ptr;
     }
 
-    reading_type_infos_.push_back(type_info);
+    reading_type_infos_.push_back(raw_ptr);
   }
 
   // Calculate size of meta section
@@ -512,7 +472,7 @@ Result<size_t, Error> ReadContext::load_type_meta(int32_t 
meta_offset) {
   return meta_section_size;
 }
 
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<const TypeInfo *, Error>
 ReadContext::get_type_info_by_index(size_t index) const {
   if (index >= reading_type_infos_.size()) {
     return Unexpected(Error::invalid(
@@ -522,7 +482,7 @@ ReadContext::get_type_info_by_index(size_t index) const {
   return reading_type_infos_[index];
 }
 
-Result<std::shared_ptr<TypeInfo>, Error> ReadContext::read_any_typeinfo() {
+Result<const TypeInfo *, Error> ReadContext::read_any_typeinfo() {
   FORY_TRY(type_id, buffer_->ReadVarUint32());
   uint32_t type_id_low = type_id & 0xff;
 
@@ -544,21 +504,13 @@ Result<std::shared_ptr<TypeInfo>, Error> 
ReadContext::read_any_typeinfo() {
              meta_string_table_.read_string(*buffer_, kNamespaceDecoder));
     FORY_TRY(type_name,
              meta_string_table_.read_string(*buffer_, kTypeNameDecoder));
-    auto type_info =
-        type_resolver_->get_type_info_by_name(namespace_str, type_name);
-    if (!type_info) {
-      return Unexpected(Error::type_error(
-          "Name harness not found: " + namespace_str + "." + type_name));
-    }
+    FORY_TRY(type_info,
+             type_resolver_->get_type_info_by_name(namespace_str, type_name));
     return type_info;
   }
   default: {
     // All types must be registered in type_resolver
-    auto type_info = type_resolver_->get_type_info_by_id(type_id);
-    if (!type_info) {
-      return Unexpected(Error::type_error("Type not found for type_id: " +
-                                          std::to_string(type_id)));
-    }
+    FORY_TRY(type_info, type_resolver_->get_type_info_by_id(type_id));
     return type_info;
   }
   }
@@ -568,6 +520,7 @@ void ReadContext::reset() {
   ref_reader_.reset();
   reading_type_infos_.clear();
   parsed_type_infos_.clear();
+  owned_reading_type_infos_.clear();
   current_dyn_depth_ = 0;
   meta_string_table_.reset();
 }
diff --git a/cpp/fory/serialization/context.h b/cpp/fory/serialization/context.h
index 5d94c4b40..9a3d0ddd1 100644
--- a/cpp/fory/serialization/context.h
+++ b/cpp/fory/serialization/context.h
@@ -76,9 +76,10 @@ private:
 /// ```
 class WriteContext {
 public:
-  /// Construct write context with configuration and shared type resolver.
+  /// Construct write context with configuration and type resolver.
+  /// Takes ownership of the type resolver.
   explicit WriteContext(const Config &config,
-                        std::shared_ptr<TypeResolver> type_resolver);
+                        std::unique_ptr<TypeResolver> type_resolver);
 
   /// Destructor
   ~WriteContext();
@@ -261,7 +262,7 @@ public:
 private:
   Buffer buffer_;
   const Config *config_;
-  std::shared_ptr<TypeResolver> type_resolver_;
+  std::unique_ptr<TypeResolver> type_resolver_;
   RefWriter ref_writer_;
   uint32_t current_dyn_depth_;
 
@@ -292,9 +293,10 @@ private:
 /// ```
 class ReadContext {
 public:
-  /// Construct read context with configuration and shared type resolver.
+  /// Construct read context with configuration and type resolver.
+  /// Takes ownership of the type resolver.
   explicit ReadContext(const Config &config,
-                       std::shared_ptr<TypeResolver> type_resolver);
+                       std::unique_ptr<TypeResolver> type_resolver);
 
   /// Destructor
   ~ReadContext();
@@ -403,12 +405,11 @@ public:
     return buffer().ReadBytes(data, length);
   }
 
-  Result<std::shared_ptr<TypeInfo>, Error>
+  Result<const TypeInfo *, Error>
   read_enum_type_info(const std::type_index &type, uint32_t base_type_id);
 
   /// Read enum type info without type_index (fast path).
-  Result<std::shared_ptr<TypeInfo>, Error>
-  read_enum_type_info(uint32_t base_type_id);
+  Result<const TypeInfo *, Error> read_enum_type_info(uint32_t base_type_id);
 
   /// Load all TypeMetas from buffer at the specified offset.
   /// After loading, the reader position is restored to where it was before.
@@ -416,8 +417,8 @@ public:
   Result<size_t, Error> load_type_meta(int32_t meta_offset);
 
   /// Get TypeInfo by meta index.
-  Result<std::shared_ptr<TypeInfo>, Error>
-  get_type_info_by_index(size_t index) const;
+  /// @return const pointer to TypeInfo if found, error otherwise
+  Result<const TypeInfo *, Error> get_type_info_by_index(size_t index) const;
 
   /// Read type information dynamically from buffer based on type ID.
   /// This mirrors Rust's read_any_typeinfo implementation.
@@ -428,8 +429,8 @@ public:
   ///   (as raw strings if share_meta is disabled, or meta_index if enabled)
   /// - Other types: look up by type_id
   ///
-  /// @return TypeInfo for the read type, or error
-  Result<std::shared_ptr<TypeInfo>, Error> read_any_typeinfo();
+  /// @return const pointer to TypeInfo for the read type, or error
+  Result<const TypeInfo *, Error> read_any_typeinfo();
 
   /// Reset context for reuse.
   void reset();
@@ -437,13 +438,17 @@ public:
 private:
   Buffer *buffer_;
   const Config *config_;
-  std::shared_ptr<TypeResolver> type_resolver_;
+  std::unique_ptr<TypeResolver> type_resolver_;
   RefReader ref_reader_;
   uint32_t current_dyn_depth_;
 
   // Meta sharing state (for compatible mode)
-  std::vector<std::shared_ptr<TypeInfo>> reading_type_infos_;
-  absl::flat_hash_map<int64_t, std::shared_ptr<TypeInfo>> parsed_type_infos_;
+  // Primary storage for TypeInfo objects created during deserialization
+  std::vector<std::unique_ptr<TypeInfo>> owned_reading_type_infos_;
+  // Index-based access (pointers to owned_reading_type_infos_ or 
type_resolver)
+  std::vector<const TypeInfo *> reading_type_infos_;
+  // Cache by meta_header (pointers to owned_reading_type_infos_)
+  absl::flat_hash_map<int64_t, const TypeInfo *> parsed_type_infos_;
 
   // Dynamic meta strings used for named type/class info.
   meta::MetaStringTable meta_string_table_;
diff --git a/cpp/fory/serialization/fory.h b/cpp/fory/serialization/fory.h
index 4621ebf86..34f69ef38 100644
--- a/cpp/fory/serialization/fory.h
+++ b/cpp/fory/serialization/fory.h
@@ -349,8 +349,8 @@ public:
     if (FORY_PREDICT_FALSE(!finalized_)) {
       ensure_finalized();
     }
-    WriteContextGuard guard(write_ctx_);
-    Buffer &buffer = write_ctx_.buffer();
+    WriteContextGuard guard(*write_ctx_);
+    Buffer &buffer = write_ctx_->buffer();
 
     FORY_RETURN_NOT_OK(serialize_impl(obj, buffer));
 
@@ -429,8 +429,8 @@ public:
           Error::unsupported("Cross-endian deserialization not yet 
supported"));
     }
 
-    read_ctx_.attach(buffer);
-    ReadContextGuard guard(read_ctx_);
+    read_ctx_->attach(buffer);
+    ReadContextGuard guard(*read_ctx_);
     return deserialize_impl<T>(buffer);
   }
 
@@ -470,8 +470,8 @@ public:
           Error::unsupported("Cross-endian deserialization not yet 
supported"));
     }
 
-    read_ctx_.attach(buffer);
-    ReadContextGuard guard(read_ctx_);
+    read_ctx_->attach(buffer);
+    ReadContextGuard guard(*read_ctx_);
     return deserialize_impl<T>(buffer);
   }
 
@@ -483,32 +483,31 @@ public:
   ///
   /// Use this for direct manipulation of the serialization context.
   /// Most users should use the serialize() methods instead.
-  WriteContext &write_context() { return write_ctx_; }
+  WriteContext &write_context() { return *write_ctx_; }
 
   /// Access the internal ReadContext (for advanced use).
   ///
   /// Use this for direct manipulation of the deserialization context.
   /// Most users should use the deserialize() methods instead.
-  ReadContext &read_context() { return read_ctx_; }
+  ReadContext &read_context() { return *read_ctx_; }
 
 private:
   /// Constructor for ForyBuilder - resolver will be finalized lazily.
   explicit Fory(const Config &config, std::shared_ptr<TypeResolver> resolver)
       : BaseFory(config, std::move(resolver)), finalized_(false),
         precomputed_header_(compute_header(config.xlang)),
-        header_length_(config.xlang ? 4 : 3),
-        write_ctx_(config_, type_resolver_),
-        read_ctx_(config_, type_resolver_) {}
+        header_length_(config.xlang ? 4 : 3) {}
 
   /// Constructor for ThreadSafeFory pool - resolver is already finalized.
   struct PreFinalized {};
   explicit Fory(const Config &config, std::shared_ptr<TypeResolver> resolver,
                 PreFinalized)
-      : BaseFory(config, std::move(resolver)), finalized_(true),
+      : BaseFory(config, std::move(resolver)), finalized_(false),
         precomputed_header_(compute_header(config.xlang)),
-        header_length_(config.xlang ? 4 : 3),
-        write_ctx_(config_, type_resolver_),
-        read_ctx_(config_, type_resolver_) {}
+        header_length_(config.xlang ? 4 : 3) {
+    // Pre-finalized, immediately create contexts
+    ensure_finalized();
+  }
 
   /// Finalize the type resolver on first use.
   void ensure_finalized() {
@@ -517,7 +516,13 @@ private:
       FORY_CHECK(final_result.ok())
           << "Failed to build finalized TypeResolver: "
           << final_result.error().to_string();
-      type_resolver_ = std::move(final_result).value();
+      // Replace with finalized resolver
+      auto finalized_resolver = std::move(final_result).value();
+      // Create contexts with cloned resolvers
+      write_ctx_.emplace(config_, finalized_resolver->clone());
+      read_ctx_.emplace(config_, finalized_resolver->clone());
+      // Store finalized resolver
+      type_resolver_ = std::move(finalized_resolver);
       finalized_ = true;
     }
   }
@@ -553,17 +558,17 @@ private:
 
     // Reserve space for meta offset in compatible mode
     size_t meta_start_offset = 0;
-    if (write_ctx_.is_compatible()) {
+    if (write_ctx_->is_compatible()) {
       meta_start_offset = buffer.writer_index();
       buffer.WriteInt32(-1); // Placeholder for meta offset (fixed 4 bytes)
     }
 
     // Top-level serialization: YES ref flags, yes type info
-    FORY_RETURN_NOT_OK(Serializer<T>::write(obj, write_ctx_, true, true));
+    FORY_RETURN_NOT_OK(Serializer<T>::write(obj, *write_ctx_, true, true));
 
     // Write collected TypeMetas at the end in compatible mode
-    if (write_ctx_.is_compatible() && !write_ctx_.meta_empty()) {
-      write_ctx_.write_meta(meta_start_offset);
+    if (write_ctx_->is_compatible() && !write_ctx_->meta_empty()) {
+      write_ctx_->write_meta(meta_start_offset);
     }
 
     return buffer.writer_index() - start_pos;
@@ -573,21 +578,21 @@ private:
   template <typename T> Result<T, Error> deserialize_impl(Buffer &buffer) {
     // Load TypeMetas at the beginning in compatible mode
     size_t bytes_to_skip = 0;
-    if (read_ctx_.is_compatible()) {
+    if (read_ctx_->is_compatible()) {
       auto meta_offset_result = buffer.ReadInt32();
       FORY_RETURN_IF_ERROR(meta_offset_result);
       int32_t meta_offset = meta_offset_result.value();
       if (meta_offset != -1) {
-        FORY_TRY(meta_size, read_ctx_.load_type_meta(meta_offset));
+        FORY_TRY(meta_size, read_ctx_->load_type_meta(meta_offset));
         bytes_to_skip = meta_size;
       }
     }
 
     // Top-level deserialization: YES ref flags, yes type info
-    auto result = Serializer<T>::read(read_ctx_, true, true);
+    auto result = Serializer<T>::read(*read_ctx_, true, true);
 
     if (result.ok()) {
-      read_ctx_.ref_reader().resolve_callbacks();
+      read_ctx_->ref_reader().resolve_callbacks();
       if (bytes_to_skip > 0) {
         buffer.IncreaseReaderIndex(static_cast<uint32_t>(bytes_to_skip));
       }
@@ -598,8 +603,8 @@ private:
   bool finalized_;
   uint32_t precomputed_header_;
   uint8_t header_length_;
-  WriteContext write_ctx_;
-  ReadContext read_ctx_;
+  std::optional<WriteContext> write_ctx_;
+  std::optional<ReadContext> read_ctx_;
 
   friend class ForyBuilder;
   friend class ThreadSafeFory;
diff --git a/cpp/fory/serialization/map_serializer.h 
b/cpp/fory/serialization/map_serializer.h
index a27c69f3a..32abb9c8e 100644
--- a/cpp/fory/serialization/map_serializer.h
+++ b/cpp/fory/serialization/map_serializer.h
@@ -61,7 +61,7 @@ inline Result<void, Error> read_type_info(ReadContext &ctx) {
   return Serializer<T>::read_type_info(ctx);
 }
 
-inline Result<std::shared_ptr<TypeInfo>, Error>
+inline Result<const TypeInfo *, Error>
 read_polymorphic_type_info(ReadContext &ctx) {
   return ctx.read_any_typeinfo();
 }
@@ -343,13 +343,15 @@ write_map_data_slow(const MapType &map, WriteContext 
&ctx, bool has_generics) {
     uint32_t val_type_id = 0;
     if constexpr (key_is_polymorphic) {
       auto concrete_type_id = get_concrete_type_id(key);
-      FORY_TRY(type_info, ctx.type_resolver().get_type_info(concrete_type_id));
-      key_type_id = type_info->type_id;
+      FORY_TRY(key_type_info,
+               ctx.type_resolver().get_type_info(concrete_type_id));
+      key_type_id = key_type_info->type_id;
     }
     if constexpr (val_is_polymorphic) {
       auto concrete_type_id = get_concrete_type_id(value);
-      FORY_TRY(type_info, ctx.type_resolver().get_type_info(concrete_type_id));
-      val_type_id = type_info->type_id;
+      FORY_TRY(val_type_info,
+               ctx.type_resolver().get_type_info(concrete_type_id));
+      val_type_id = val_type_info->type_id;
     }
 
     // Check if we need to start a new chunk due to type changes
@@ -597,11 +599,11 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
       }
 
       // Now read type info if needed
-      std::shared_ptr<TypeInfo> value_type_info = nullptr;
+      const TypeInfo *value_type_info = nullptr;
       if (!value_declared || val_is_polymorphic) {
         if constexpr (val_is_polymorphic) {
           FORY_TRY(type_info, read_polymorphic_type_info(ctx));
-          value_type_info = std::move(type_info);
+          value_type_info = type_info;
         } else {
           FORY_RETURN_NOT_OK(read_type_info<V>(ctx));
         }
@@ -646,11 +648,11 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
       }
 
       // Now read type info if needed
-      std::shared_ptr<TypeInfo> key_type_info = nullptr;
+      const TypeInfo *key_type_info = nullptr;
       if (!key_declared || key_is_polymorphic) {
         if constexpr (key_is_polymorphic) {
           FORY_TRY(type_info, read_polymorphic_type_info(ctx));
-          key_type_info = std::move(type_info);
+          key_type_info = type_info;
         } else {
           FORY_RETURN_NOT_OK(read_type_info<K>(ctx));
         }
@@ -681,13 +683,13 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
     bool track_value_ref = (header & TRACKING_VALUE_REF) != 0;
 
     // Read type info if not declared
-    std::shared_ptr<TypeInfo> key_type_info = nullptr;
-    std::shared_ptr<TypeInfo> value_type_info = nullptr;
+    const TypeInfo *key_type_info = nullptr;
+    const TypeInfo *value_type_info = nullptr;
 
     if (!key_declared || key_is_polymorphic) {
       if constexpr (key_is_polymorphic) {
         FORY_TRY(type_info, read_polymorphic_type_info(ctx));
-        key_type_info = std::move(type_info);
+        key_type_info = type_info;
       } else {
         FORY_RETURN_NOT_OK(read_type_info<K>(ctx));
       }
@@ -695,7 +697,7 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
     if (!value_declared || val_is_polymorphic) {
       if constexpr (val_is_polymorphic) {
         FORY_TRY(type_info, read_polymorphic_type_info(ctx));
-        value_type_info = std::move(type_info);
+        value_type_info = type_info;
       } else {
         FORY_RETURN_NOT_OK(read_type_info<V>(ctx));
       }
diff --git a/cpp/fory/serialization/serialization_test.cc 
b/cpp/fory/serialization/serialization_test.cc
index 5c708bf8c..bf9d7e17d 100644
--- a/cpp/fory/serialization/serialization_test.cc
+++ b/cpp/fory/serialization/serialization_test.cc
@@ -91,6 +91,11 @@ inline void register_test_types(Fory &fory) {
   fory.register_struct<::SimpleStruct>(type_id++);
   fory.register_struct<::ComplexStruct>(type_id++);
   fory.register_struct<::NestedStruct>(type_id++);
+
+  // Register all enum types used in tests (register_struct works for enums 
too)
+  fory.register_struct<Color>(type_id++);
+  fory.register_struct<LegacyStatus>(type_id++);
+  fory.register_struct<OldStatus>(type_id++);
 }
 
 template <typename T>
@@ -195,6 +200,7 @@ TEST(SerializationTest, OldEnumRoundtrip) {
 
 TEST(SerializationTest, EnumSerializesOrdinalValue) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  fory.register_struct<LegacyStatus>(1);
 
   auto bytes_result = fory.serialize(LegacyStatus::LARGE);
   ASSERT_TRUE(bytes_result.ok())
@@ -202,48 +208,57 @@ TEST(SerializationTest, EnumSerializesOrdinalValue) {
 
   std::vector<uint8_t> bytes = bytes_result.value();
   // Xlang spec: enums are serialized as varuint32, not fixed int32_t
-  // Expected: 4 (header) + 1 (ref flag) + 1 (type id) + 1 (ordinal as
-  // varuint32) = 7 bytes
-  ASSERT_GE(bytes.size(), 4 + 1 + 1 + 1);
+  // With registration, type_id = (1 << 8) + ENUM = 269, which takes 2 bytes as
+  // varuint32 Expected: 4 (header) + 1 (ref flag) + 2 (type id as varuint) + 1
+  // (ordinal as varuint32) = 8 bytes
+  ASSERT_GE(bytes.size(), 4 + 1 + 2 + 1);
   size_t offset = 4;
   EXPECT_EQ(bytes[offset], static_cast<uint8_t>(NOT_NULL_VALUE_FLAG));
-  EXPECT_EQ(bytes[offset + 1], static_cast<uint8_t>(TypeId::ENUM));
+  // Type ID 269 = (1 << 8) + ENUM encoded as varuint32: 0x8D, 0x02
+  EXPECT_EQ(bytes[offset + 1], 0x8D);
+  EXPECT_EQ(bytes[offset + 2], 0x02);
   // Ordinal 2 encoded as varuint32 is just 1 byte with value 2
-  EXPECT_EQ(bytes[offset + 2], 2);
+  EXPECT_EQ(bytes[offset + 3], 2);
 }
 
 TEST(SerializationTest, OldEnumSerializesOrdinalValue) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  fory.register_struct<OldStatus>(1);
 
   auto bytes_result = fory.serialize(OldStatus::OLD_POS);
   ASSERT_TRUE(bytes_result.ok())
       << "Serialization failed: " << bytes_result.error().to_string();
 
   std::vector<uint8_t> bytes = bytes_result.value();
-  // Xlang spec: enums are serialized as varuint32, not fixed int32_t
-  ASSERT_GE(bytes.size(), 4 + 1 + 1 + 1);
+  // With registration, type_id = (1 << 8) + ENUM = 269, which takes 2 bytes
+  ASSERT_GE(bytes.size(), 4 + 1 + 2 + 1);
   size_t offset = 4;
   EXPECT_EQ(bytes[offset], static_cast<uint8_t>(NOT_NULL_VALUE_FLAG));
-  EXPECT_EQ(bytes[offset + 1], static_cast<uint8_t>(TypeId::ENUM));
+  // Type ID 269 encoded as varuint32: 0x8D, 0x02
+  EXPECT_EQ(bytes[offset + 1], 0x8D);
+  EXPECT_EQ(bytes[offset + 2], 0x02);
   // Ordinal 2 encoded as varuint32 is just 1 byte with value 2
-  EXPECT_EQ(bytes[offset + 2], 2);
+  EXPECT_EQ(bytes[offset + 3], 2);
 }
 
 TEST(SerializationTest, EnumOrdinalMappingHandlesNonZeroStart) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  fory.register_struct<LegacyStatus>(1);
 
   auto bytes_result = fory.serialize(LegacyStatus::NEG);
   ASSERT_TRUE(bytes_result.ok())
       << "Serialization failed: " << bytes_result.error().to_string();
 
   std::vector<uint8_t> bytes = bytes_result.value();
-  // Xlang spec: enums are serialized as varuint32, not fixed int32_t
-  ASSERT_GE(bytes.size(), 4 + 1 + 1 + 1);
+  // With registration, type_id = (1 << 8) + ENUM = 269, which takes 2 bytes
+  ASSERT_GE(bytes.size(), 4 + 1 + 2 + 1);
   size_t offset = 4;
   EXPECT_EQ(bytes[offset], static_cast<uint8_t>(NOT_NULL_VALUE_FLAG));
-  EXPECT_EQ(bytes[offset + 1], static_cast<uint8_t>(TypeId::ENUM));
+  // Type ID 269 encoded as varuint32: 0x8D, 0x02
+  EXPECT_EQ(bytes[offset + 1], 0x8D);
+  EXPECT_EQ(bytes[offset + 2], 0x02);
   // Ordinal 0 encoded as varuint32 is just 1 byte with value 0
-  EXPECT_EQ(bytes[offset + 2], 0);
+  EXPECT_EQ(bytes[offset + 3], 0);
 
   auto roundtrip = fory.deserialize<LegacyStatus>(bytes.data(), bytes.size());
   ASSERT_TRUE(roundtrip.ok())
@@ -253,6 +268,7 @@ TEST(SerializationTest, 
EnumOrdinalMappingHandlesNonZeroStart) {
 
 TEST(SerializationTest, EnumOrdinalMappingRejectsInvalidOrdinal) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  fory.register_struct<LegacyStatus>(1);
 
   auto bytes_result = fory.serialize(LegacyStatus::NEG);
   ASSERT_TRUE(bytes_result.ok())
@@ -260,8 +276,9 @@ TEST(SerializationTest, 
EnumOrdinalMappingRejectsInvalidOrdinal) {
 
   std::vector<uint8_t> bytes = bytes_result.value();
   size_t offset = 4;
+  // With registration, type_id takes 2 bytes, ordinal is at offset + 3
   // Replace the valid ordinal with an invalid one (99 as varuint32)
-  bytes[offset + 2] = 99;
+  bytes[offset + 3] = 99;
 
   auto decode = fory.deserialize<LegacyStatus>(bytes.data(), bytes.size());
   EXPECT_FALSE(decode.ok());
@@ -269,19 +286,22 @@ TEST(SerializationTest, 
EnumOrdinalMappingRejectsInvalidOrdinal) {
 
 TEST(SerializationTest, OldEnumOrdinalMappingHandlesNonZeroStart) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  fory.register_struct<OldStatus>(1);
 
   auto bytes_result = fory.serialize(OldStatus::OLD_NEG);
   ASSERT_TRUE(bytes_result.ok())
       << "Serialization failed: " << bytes_result.error().to_string();
 
   std::vector<uint8_t> bytes = bytes_result.value();
-  // Xlang spec: enums are serialized as varuint32, not fixed int32_t
-  ASSERT_GE(bytes.size(), 4 + 1 + 1 + 1);
+  // With registration, type_id = (1 << 8) + ENUM = 269, which takes 2 bytes
+  ASSERT_GE(bytes.size(), 4 + 1 + 2 + 1);
   size_t offset = 4;
   EXPECT_EQ(bytes[offset], static_cast<uint8_t>(NOT_NULL_VALUE_FLAG));
-  EXPECT_EQ(bytes[offset + 1], static_cast<uint8_t>(TypeId::ENUM));
+  // Type ID 269 encoded as varuint32: 0x8D, 0x02
+  EXPECT_EQ(bytes[offset + 1], 0x8D);
+  EXPECT_EQ(bytes[offset + 2], 0x02);
   // Ordinal 0 encoded as varuint32 is just 1 byte with value 0
-  EXPECT_EQ(bytes[offset + 2], 0);
+  EXPECT_EQ(bytes[offset + 3], 0);
 
   auto roundtrip = fory.deserialize<OldStatus>(bytes.data(), bytes.size());
   ASSERT_TRUE(roundtrip.ok())
diff --git a/cpp/fory/serialization/skip.cc b/cpp/fory/serialization/skip.cc
index 92626cd6b..af073b476 100644
--- a/cpp/fory/serialization/skip.cc
+++ b/cpp/fory/serialization/skip.cc
@@ -223,7 +223,7 @@ Result<void, Error> skip_ext(ReadContext &ctx, const 
FieldType &field_type) {
   uint32_t low = full_type_id & 0xffu;
   TypeId tid = static_cast<TypeId>(low);
 
-  std::shared_ptr<TypeInfo> type_info;
+  const TypeInfo *type_info = nullptr;
 
   if (tid == TypeId::NAMED_EXT) {
     // Named ext: read type_id and meta_index
@@ -236,7 +236,9 @@ Result<void, Error> skip_ext(ReadContext &ctx, const 
FieldType &field_type) {
   } else {
     // ID-based ext: look up by full type_id
     // The ext fields in TypeMeta store the user type_id (high bits | EXT)
-    type_info = ctx.type_resolver().get_type_info_by_id(full_type_id);
+    FORY_TRY(type_info_by_id,
+             ctx.type_resolver().get_type_info_by_id(full_type_id));
+    type_info = type_info_by_id;
   }
 
   if (!type_info) {
diff --git a/cpp/fory/serialization/struct_serializer.h 
b/cpp/fory/serialization/struct_serializer.h
index bca4e027b..e9b33ee3c 100644
--- a/cpp/fory/serialization/struct_serializer.h
+++ b/cpp/fory/serialization/struct_serializer.h
@@ -1106,7 +1106,7 @@ Result<void, Error> read_struct_fields_impl(T &obj, 
ReadContext &ctx,
 template <typename T, size_t... Indices>
 Result<void, Error>
 read_struct_fields_compatible(T &obj, ReadContext &ctx,
-                              const std::shared_ptr<TypeMeta> 
&remote_type_meta,
+                              const TypeMeta *remote_type_meta,
                               std::index_sequence<Indices...>) {
 
   using Helpers = CompileTimeFieldHelpers<T>;
@@ -1184,14 +1184,13 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
   /// This is used by collection serializers to write element type info.
   /// Matches Rust's struct_::write_type_info.
   static Result<void, Error> write_type_info(WriteContext &ctx) {
-    FORY_TRY(type_info, ctx.type_resolver().template 
get_struct_type_info<T>());
-    FORY_TRY(type_id, ctx.type_resolver().template get_type_id<T>());
-    ctx.write_varuint32(type_id);
+    FORY_TRY(type_info, ctx.type_resolver().template get_type_info<T>());
+    ctx.write_varuint32(type_info->type_id);
 
     // In compatible mode, always write meta index (matches Rust behavior)
     if (ctx.is_compatible() && type_info->type_meta) {
       // Use TypeInfo* overload to avoid type_index creation
-      size_t meta_index = ctx.push_meta(type_info.get());
+      size_t meta_index = ctx.push_meta(type_info);
       ctx.write_varuint32(static_cast<uint32_t>(meta_index));
     }
     return Result<void, Error>();
@@ -1215,11 +1214,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
     if (write_type) {
       // Direct lookup using compile-time type_index<T>() - O(1) hash lookup
-      auto info_result = ctx.type_resolver().template 
get_struct_type_info<T>();
-      if (FORY_PREDICT_FALSE(!info_result.ok())) {
-        return Unexpected(info_result.error());
-      }
-      const TypeInfo *type_info = info_result.value().get();
+      FORY_TRY(type_info, ctx.type_resolver().template get_type_info<T>());
       uint32_t tid = type_info->type_id;
 
       // Fast path: check if this is a simple STRUCT type (no meta needed)
@@ -1237,8 +1232,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
   static Result<void, Error> write_data(const T &obj, WriteContext &ctx) {
     if (ctx.check_struct_version()) {
-      FORY_TRY(type_info,
-               ctx.type_resolver().template get_struct_type_info<T>());
+      FORY_TRY(type_info, ctx.type_resolver().template get_type_info<T>());
       if (!type_info->type_meta) {
         return Unexpected(Error::type_error(
             "Type metadata not initialized for requested struct"));
@@ -1257,8 +1251,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
   static Result<void, Error> write_data_generic(const T &obj, WriteContext 
&ctx,
                                                 bool has_generics) {
     if (ctx.check_struct_version()) {
-      FORY_TRY(type_info,
-               ctx.type_resolver().template get_struct_type_info<T>());
+      FORY_TRY(type_info, ctx.type_resolver().template get_type_info<T>());
       if (!type_info->type_meta) {
         return Unexpected(Error::type_error(
             "Type metadata not initialized for requested struct"));
@@ -1307,9 +1300,8 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
           // Check LOCAL type to decide if we should read meta_index (matches
           // Rust logic)
           FORY_TRY(local_type_info,
-                   ctx.type_resolver().template get_struct_type_info<T>());
-          uint32_t local_type_id =
-              ctx.type_resolver().get_type_id(*local_type_info);
+                   ctx.type_resolver().template get_type_info<T>());
+          uint32_t local_type_id = local_type_info->type_id;
           uint8_t local_type_id_low = local_type_id & 0xff;
 
           if (local_type_id_low ==
@@ -1347,11 +1339,8 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
         // the expected static type.
         if (read_type) {
           // Direct lookup using compile-time type_index<T>() - O(1) hash 
lookup
-          auto type_id_result = ctx.type_resolver().template get_type_id<T>();
-          if (FORY_PREDICT_FALSE(!type_id_result.ok())) {
-            return Unexpected(std::move(type_id_result).error());
-          }
-          uint32_t expected_type_id = type_id_result.value();
+          FORY_TRY(type_info, ctx.type_resolver().template get_type_info<T>());
+          uint32_t expected_type_id = type_info->type_id;
 
           // FAST PATH: For simple numeric type IDs (not named types), we can
           // just read the varint and compare directly without hash lookup.
@@ -1395,14 +1384,13 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
     }
   }
 
-  static Result<T, Error>
-  read_compatible(ReadContext &ctx,
-                  std::shared_ptr<TypeInfo> remote_type_info) {
+  static Result<T, Error> read_compatible(ReadContext &ctx,
+                                          const TypeInfo *remote_type_info) {
     // Read and verify struct version if enabled (matches write_data behavior)
     if (ctx.check_struct_version()) {
       FORY_TRY(read_version, ctx.buffer().ReadInt32());
       FORY_TRY(local_type_info,
-               ctx.type_resolver().template get_struct_type_info<T>());
+               ctx.type_resolver().template get_type_info<T>());
       if (!local_type_info->type_meta) {
         return Unexpected(Error::type_error(
             "Type metadata not initialized for requested struct"));
@@ -1425,7 +1413,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
     // Use remote TypeMeta for schema evolution - field IDs already assigned
     FORY_RETURN_NOT_OK(detail::read_struct_fields_compatible(
-        obj, ctx, remote_type_info->type_meta,
+        obj, ctx, remote_type_info->type_meta.get(),
         std::make_index_sequence<field_count>{}));
 
     return obj;
@@ -1435,7 +1423,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
     if (ctx.check_struct_version()) {
       FORY_TRY(read_version, ctx.buffer().ReadInt32());
       FORY_TRY(local_type_info,
-               ctx.type_resolver().template get_struct_type_info<T>());
+               ctx.type_resolver().template get_type_info<T>());
       if (!local_type_info->type_meta) {
         return Unexpected(Error::type_error(
             "Type metadata not initialized for requested struct"));
@@ -1469,8 +1457,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
     // In compatible mode with type info provided, use schema evolution path
     if (ctx.is_compatible() && type_info.type_meta) {
-      auto remote_type_info = std::make_shared<TypeInfo>(type_info);
-      return read_compatible(ctx, remote_type_info);
+      return read_compatible(ctx, &type_info);
     }
 
     // Otherwise use normal read path
diff --git a/cpp/fory/serialization/type_info.h 
b/cpp/fory/serialization/type_info.h
index 0715ca763..9f34a8d66 100644
--- a/cpp/fory/serialization/type_info.h
+++ b/cpp/fory/serialization/type_info.h
@@ -106,15 +106,29 @@ struct TypeInfo {
   std::string type_name;
   bool register_by_name = false;
   bool is_external = false;
-  std::shared_ptr<TypeMeta> type_meta;
+  std::unique_ptr<TypeMeta> type_meta;
   std::vector<size_t> sorted_indices;
   absl::flat_hash_map<std::string, size_t> name_to_index;
   std::vector<uint8_t> type_def;
   Harness harness;
   // Pre-encoded meta strings for efficient writing (avoids re-encoding on each
   // write)
-  std::shared_ptr<CachedMetaString> encoded_namespace;
-  std::shared_ptr<CachedMetaString> encoded_type_name;
+  std::unique_ptr<CachedMetaString> encoded_namespace;
+  std::unique_ptr<CachedMetaString> encoded_type_name;
+
+  TypeInfo() = default;
+
+  // Non-copyable due to unique_ptr members
+  TypeInfo(const TypeInfo &) = delete;
+  TypeInfo &operator=(const TypeInfo &) = delete;
+
+  // Movable
+  TypeInfo(TypeInfo &&) = default;
+  TypeInfo &operator=(TypeInfo &&) = default;
+
+  /// Creates a deep clone of this TypeInfo.
+  /// All unique_ptr members are cloned into new instances.
+  std::unique_ptr<TypeInfo> deep_clone() const;
 };
 
 } // namespace serialization
diff --git a/cpp/fory/serialization/type_resolver.cc 
b/cpp/fory/serialization/type_resolver.cc
index 63babe58c..98f5c1341 100644
--- a/cpp/fory/serialization/type_resolver.cc
+++ b/cpp/fory/serialization/type_resolver.cc
@@ -336,7 +336,7 @@ Result<std::vector<uint8_t>, Error> TypeMeta::to_bytes() 
const {
                                   result_buffer.writer_index());
 }
 
-Result<std::shared_ptr<TypeMeta>, Error>
+Result<std::unique_ptr<TypeMeta>, Error>
 TypeMeta::from_bytes(Buffer &buffer, const TypeMeta *local_type_info) {
   size_t start_pos = buffer.reader_index();
 
@@ -411,7 +411,7 @@ TypeMeta::from_bytes(Buffer &buffer, const TypeMeta 
*local_type_info) {
     buffer.IncreaseReaderIndex(remaining);
   }
 
-  auto meta = std::make_shared<TypeMeta>();
+  auto meta = std::make_unique<TypeMeta>();
   meta->hash = meta_hash;
   meta->type_id = type_id;
   meta->namespace_str = std::move(namespace_str);
@@ -422,7 +422,7 @@ TypeMeta::from_bytes(Buffer &buffer, const TypeMeta 
*local_type_info) {
   return meta;
 }
 
-Result<std::shared_ptr<TypeMeta>, Error>
+Result<std::unique_ptr<TypeMeta>, Error>
 TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t header) {
   int64_t meta_size = header & META_SIZE_MASK;
   size_t header_size = 0;
@@ -489,7 +489,7 @@ TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t 
header) {
     buffer.IncreaseReaderIndex(remaining);
   }
 
-  auto meta = std::make_shared<TypeMeta>();
+  auto meta = std::make_unique<TypeMeta>();
   meta->hash = meta_hash;
   meta->type_id = type_id;
   meta->namespace_str = std::move(namespace_str);
@@ -904,75 +904,87 @@ int32_t TypeMeta::compute_struct_version(const TypeMeta 
&meta) {
 }
 
 // ============================================================================
-// TypeResolver::read_any_typeinfo Implementation
+// TypeInfo::deep_clone Implementation
 // ============================================================================
 
-Result<std::shared_ptr<TypeInfo>, Error>
-TypeResolver::read_any_typeinfo(ReadContext &ctx) {
-  return read_any_typeinfo(ctx, nullptr);
+std::unique_ptr<TypeInfo> TypeInfo::deep_clone() const {
+  auto cloned = std::make_unique<TypeInfo>();
+  cloned->type_id = type_id;
+  cloned->namespace_name = namespace_name;
+  cloned->type_name = type_name;
+  cloned->register_by_name = register_by_name;
+  cloned->is_external = is_external;
+  cloned->sorted_indices = sorted_indices;
+  cloned->name_to_index = name_to_index;
+  cloned->type_def = type_def;
+  cloned->harness = harness;
+
+  // Deep clone unique_ptr members
+  if (type_meta) {
+    cloned->type_meta = std::make_unique<TypeMeta>(*type_meta);
+  }
+  if (encoded_namespace) {
+    cloned->encoded_namespace = std::make_unique<CachedMetaString>();
+    cloned->encoded_namespace->original = encoded_namespace->original;
+    cloned->encoded_namespace->bytes = encoded_namespace->bytes;
+    cloned->encoded_namespace->encoding = encoded_namespace->encoding;
+    cloned->encoded_namespace->hash = encoded_namespace->hash;
+  }
+  if (encoded_type_name) {
+    cloned->encoded_type_name = std::make_unique<CachedMetaString>();
+    cloned->encoded_type_name->original = encoded_type_name->original;
+    cloned->encoded_type_name->bytes = encoded_type_name->bytes;
+    cloned->encoded_type_name->encoding = encoded_type_name->encoding;
+    cloned->encoded_type_name->hash = encoded_type_name->hash;
+  }
+
+  return cloned;
 }
 
-Result<std::shared_ptr<TypeInfo>, Error>
-TypeResolver::read_any_typeinfo(ReadContext &ctx,
-                                const TypeMeta *local_type_meta) {
-  FORY_TRY(fory_type_id, ctx.read_varuint32());
-  uint32_t type_id_low = fory_type_id & 0xff;
-
-  // Handle compatible struct types (with embedded TypeMeta)
-  if (type_id_low == static_cast<uint32_t>(TypeId::NAMED_COMPATIBLE_STRUCT) ||
-      type_id_low == static_cast<uint32_t>(TypeId::COMPATIBLE_STRUCT)) {
-    // Use provided local_type_meta if available, otherwise try to get from
-    // registry
-    if (local_type_meta == nullptr) {
-      auto local_type_info = get_type_info_by_id(fory_type_id);
-      if (local_type_info && local_type_info->type_meta) {
-        local_type_meta = local_type_info->type_meta.get();
-      }
-    }
+// ============================================================================
+// encode_meta_string Implementation
+// ============================================================================
 
-    // Read the embedded TypeMeta from stream
-    // Pass local_type_meta so that assign_field_ids is called during parsing
-    FORY_TRY(remote_meta, TypeMeta::from_bytes(ctx.buffer(), local_type_meta));
+Result<std::unique_ptr<CachedMetaString>, Error>
+encode_meta_string(const std::string &value, bool is_namespace) {
+  auto cached = std::make_unique<CachedMetaString>();
+  cached->original = value;
+
+  if (value.empty()) {
+    // For empty strings, use a minimal encoding
+    cached->encoding = 0; // UTF8
+    cached->bytes.clear();
+    cached->hash = 0;
+    return cached;
+  }
 
-    // Create a temporary TypeInfo with the remote TypeMeta
-    auto type_info = std::make_shared<TypeInfo>();
-    type_info->type_id = fory_type_id;
-    type_info->type_meta = remote_meta;
-    // Note: We don't have type_def here since this is remote schema
+  // Use MetaStringEncoder to encode the string
+  static const MetaStringEncoder kNamespaceEncoder('.', '_');
+  static const MetaStringEncoder kTypeNameEncoder('$', '_');
 
-    return type_info;
-  }
+  static const std::vector<MetaEncoding> kNamespaceEncodings = {
+      MetaEncoding::UTF8, MetaEncoding::ALL_TO_LOWER_SPECIAL,
+      MetaEncoding::LOWER_UPPER_DIGIT_SPECIAL};
 
-  // Handle named types (namespace + type name)
-  if (type_id_low == static_cast<uint32_t>(TypeId::NAMED_ENUM) ||
-      type_id_low == static_cast<uint32_t>(TypeId::NAMED_EXT) ||
-      type_id_low == static_cast<uint32_t>(TypeId::NAMED_STRUCT)) {
-    // TODO: If share_meta is enabled, read meta_index instead
-    // For now, read namespace and type name
-    FORY_TRY(ns_len, ctx.read_varuint32());
-    std::string namespace_str(ns_len, '\0');
-    FORY_RETURN_NOT_OK(ctx.read_bytes(namespace_str.data(), ns_len));
-
-    FORY_TRY(name_len, ctx.read_varuint32());
-    std::string type_name(name_len, '\0');
-    FORY_RETURN_NOT_OK(ctx.read_bytes(type_name.data(), name_len));
-
-    auto type_info = get_type_info_by_name(namespace_str, type_name);
-    if (type_info) {
-      return type_info;
-    }
-    return Unexpected(Error::type_error("Type info not found for namespace '" +
-                                        namespace_str + "' and type name '" +
-                                        type_name + "'"));
-  }
+  static const std::vector<MetaEncoding> kTypeNameEncodings = {
+      MetaEncoding::UTF8, MetaEncoding::ALL_TO_LOWER_SPECIAL,
+      MetaEncoding::LOWER_UPPER_DIGIT_SPECIAL,
+      MetaEncoding::FIRST_TO_LOWER_SPECIAL};
 
-  // Handle other types by ID lookup
-  auto type_info = get_type_info_by_id(fory_type_id);
-  if (type_info) {
-    return type_info;
+  if (is_namespace) {
+    FORY_TRY(result, kNamespaceEncoder.encode(value, kNamespaceEncodings));
+    cached->encoding = static_cast<uint8_t>(result.encoding);
+    cached->bytes = std::move(result.bytes);
+  } else {
+    FORY_TRY(result, kTypeNameEncoder.encode(value, kTypeNameEncodings));
+    cached->encoding = static_cast<uint8_t>(result.encoding);
+    cached->bytes = std::move(result.bytes);
   }
-  return Unexpected(Error::type_error("Type info not found for type_id: " +
-                                      std::to_string(fory_type_id)));
+
+  // Compute hash if needed (for now, just use 0)
+  cached->hash = 0;
+
+  return cached;
 }
 
 Result<const TypeInfo *, Error>
@@ -982,12 +994,12 @@ TypeResolver::get_type_info(const std::type_index 
&type_index) const {
   if (it == type_info_by_runtime_type_.end()) {
     return Unexpected(Error::type_error("TypeInfo not found for type_index"));
   }
-  return it->second.get();
+  return it->second;
 }
 
-Result<std::shared_ptr<TypeResolver>, Error>
+Result<std::unique_ptr<TypeResolver>, Error>
 TypeResolver::build_final_type_resolver() {
-  auto final_resolver = std::make_shared<TypeResolver>();
+  auto final_resolver = std::make_unique<TypeResolver>();
 
   // Copy configuration
   final_resolver->compatible_ = compatible_;
@@ -996,16 +1008,40 @@ TypeResolver::build_final_type_resolver() {
   final_resolver->track_ref_ = track_ref_;
   final_resolver->finalized_ = true;
 
-  // Copy all existing type info maps
-  final_resolver->type_info_by_ctid_ = type_info_by_ctid_;
-  final_resolver->type_info_by_id_ = type_info_by_id_;
-  final_resolver->type_info_by_name_ = type_info_by_name_;
-  final_resolver->type_info_by_runtime_type_ = type_info_by_runtime_type_;
+  // Build mapping from old pointers to new pointers for rebuilding lookup maps
+  absl::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
+
+  // Deep clone all existing TypeInfo objects
+  for (const auto &info : type_infos_) {
+    auto cloned = info->deep_clone();
+    TypeInfo *new_ptr = cloned.get();
+    ptr_map[info.get()] = new_ptr;
+    final_resolver->type_infos_.push_back(std::move(cloned));
+  }
+
+  // Rebuild lookup maps with new pointers
+  for (const auto &[key, old_ptr] : type_info_by_ctid_) {
+    final_resolver->type_info_by_ctid_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_id_) {
+    final_resolver->type_info_by_id_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_name_) {
+    final_resolver->type_info_by_name_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_runtime_type_) {
+    final_resolver->type_info_by_runtime_type_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : partial_type_infos_) {
+    final_resolver->partial_type_infos_[key] = ptr_map[old_ptr];
+  }
 
   // Process all partial type infos to build complete type metadata
-  for (const auto &[rust_type_id, partial_info] : partial_type_infos_) {
+  for (const auto &[rust_type_id, partial_ptr] :
+       final_resolver->partial_type_infos_) {
     // Call the harness's sorted_field_infos function to get complete field 
info
-    auto fields_result = partial_info->harness.sorted_field_infos_fn(*this);
+    auto fields_result =
+        partial_ptr->harness.sorted_field_infos_fn(*final_resolver);
     if (!fields_result.ok()) {
       return Unexpected(fields_result.error());
     }
@@ -1013,8 +1049,8 @@ TypeResolver::build_final_type_resolver() {
 
     // Build complete TypeMeta
     TypeMeta meta = TypeMeta::from_fields(
-        partial_info->type_id, partial_info->namespace_name,
-        partial_info->type_name, partial_info->register_by_name,
+        partial_ptr->type_id, partial_ptr->namespace_name,
+        partial_ptr->type_name, partial_ptr->register_by_name,
         std::move(sorted_fields));
 
     // Serialize TypeMeta to bytes
@@ -1023,32 +1059,18 @@ TypeResolver::build_final_type_resolver() {
       return Unexpected(type_def_result.error());
     }
 
-    // Create complete TypeInfo
-    auto complete_info = std::make_shared<TypeInfo>(*partial_info);
-    complete_info->type_def = std::move(type_def_result).value();
+    // Update the TypeInfo in place
+    partial_ptr->type_def = std::move(type_def_result).value();
 
-    // Parse the serialized TypeMeta back to create shared_ptr<TypeMeta>
-    Buffer buffer(complete_info->type_def.data(),
-                  static_cast<uint32_t>(complete_info->type_def.size()), 
false);
-    buffer.WriterIndex(static_cast<uint32_t>(complete_info->type_def.size()));
+    // Parse the serialized TypeMeta back to create unique_ptr<TypeMeta>
+    Buffer buffer(partial_ptr->type_def.data(),
+                  static_cast<uint32_t>(partial_ptr->type_def.size()), false);
+    buffer.WriterIndex(static_cast<uint32_t>(partial_ptr->type_def.size()));
     auto parsed_meta_result = TypeMeta::from_bytes(buffer, nullptr);
     if (!parsed_meta_result.ok()) {
       return Unexpected(parsed_meta_result.error());
     }
-    complete_info->type_meta = std::move(parsed_meta_result).value();
-
-    // Update all maps with complete info
-    final_resolver->type_info_by_ctid_[rust_type_id] = complete_info;
-
-    if (complete_info->type_id != 0) {
-      final_resolver->type_info_by_id_[complete_info->type_id] = complete_info;
-    }
-
-    if (complete_info->register_by_name) {
-      auto key = make_name_key(complete_info->namespace_name,
-                               complete_info->type_name);
-      final_resolver->type_info_by_name_[key] = complete_info;
-    }
+    partial_ptr->type_meta = std::move(parsed_meta_result).value();
   }
 
   // Clear partial_type_infos in the final resolver since they're all completed
@@ -1057,8 +1079,8 @@ TypeResolver::build_final_type_resolver() {
   return final_resolver;
 }
 
-std::shared_ptr<TypeResolver> TypeResolver::clone() const {
-  auto cloned = std::make_shared<TypeResolver>();
+std::unique_ptr<TypeResolver> TypeResolver::clone() const {
+  auto cloned = std::make_unique<TypeResolver>();
 
   // Copy configuration
   cloned->compatible_ = compatible_;
@@ -1067,13 +1089,32 @@ std::shared_ptr<TypeResolver> TypeResolver::clone() 
const {
   cloned->track_ref_ = track_ref_;
   cloned->finalized_ = finalized_;
 
-  // Shallow copy all maps (shared_ptr sharing)
-  cloned->type_info_by_ctid_ = type_info_by_ctid_;
-  cloned->type_info_by_id_ = type_info_by_id_;
-  cloned->type_info_by_name_ = type_info_by_name_;
-  cloned->type_info_by_runtime_type_ = type_info_by_runtime_type_;
-  // Don't copy partial_type_infos_ - clone should only be used on finalized
-  // resolvers
+  // Build mapping from old pointers to new pointers
+  absl::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
+
+  // Deep clone all TypeInfo objects
+  for (const auto &info : type_infos_) {
+    auto cloned_info = info->deep_clone();
+    TypeInfo *new_ptr = cloned_info.get();
+    ptr_map[info.get()] = new_ptr;
+    cloned->type_infos_.push_back(std::move(cloned_info));
+  }
+
+  // Rebuild lookup maps with new pointers
+  for (const auto &[key, old_ptr] : type_info_by_ctid_) {
+    cloned->type_info_by_ctid_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_id_) {
+    cloned->type_info_by_id_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_name_) {
+    cloned->type_info_by_name_[key] = ptr_map[old_ptr];
+  }
+  for (const auto &[key, old_ptr] : type_info_by_runtime_type_) {
+    cloned->type_info_by_runtime_type_[key] = ptr_map[old_ptr];
+  }
+  // Note: Don't copy partial_type_infos_ - clone should only be used on
+  // finalized resolvers
 
   return cloned;
 }
@@ -1082,11 +1123,13 @@ void TypeResolver::register_builtin_types() {
   // Register internal type IDs without harnesses (deserialization is static)
   // These are needed so read_any_typeinfo can find them by type_id
   auto register_type_id_only = [this](TypeId type_id) {
-    auto info = std::make_shared<TypeInfo>();
+    auto info = std::make_unique<TypeInfo>();
     info->type_id = static_cast<uint32_t>(type_id);
     info->register_by_name = false;
     info->is_external = false;
-    type_info_by_id_[info->type_id] = info;
+    TypeInfo *raw_ptr = info.get();
+    type_infos_.push_back(std::move(info));
+    type_info_by_id_[raw_ptr->type_id] = raw_ptr;
   };
 
   // Primitive types
diff --git a/cpp/fory/serialization/type_resolver.h 
b/cpp/fory/serialization/type_resolver.h
index 733e46cfd..0ba949de4 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -183,13 +183,13 @@ public:
   /// Read type meta from buffer (for deserialization)
   /// @param buffer Source buffer
   /// @param local_type_info Local type information (for field ID assignment)
-  static Result<std::shared_ptr<TypeMeta>, Error>
+  static Result<std::unique_ptr<TypeMeta>, Error>
   from_bytes(Buffer &buffer, const TypeMeta *local_type_info);
 
   /// Read type meta from buffer with pre-read header
   /// @param buffer Source buffer (positioned after header)
   /// @param header Pre-read 8-byte header
-  static Result<std::shared_ptr<TypeMeta>, Error>
+  static Result<std::unique_ptr<TypeMeta>, Error>
   from_bytes_with_header(Buffer &buffer, int64_t header);
 
   /// Skip type meta in buffer without parsing
@@ -416,7 +416,7 @@ std::vector<FieldInfo> 
build_field_infos(std::index_sequence<Indices...>) {
 
 /// Encode a meta string for namespace or type_name using the appropriate
 /// encoder. This is called during registration to pre-compute the encoded 
form.
-Result<std::shared_ptr<CachedMetaString>, Error>
+Result<std::unique_ptr<CachedMetaString>, Error>
 encode_meta_string(const std::string &value, bool is_namespace);
 
 // ============================================================================
@@ -441,37 +441,16 @@ public:
   template <typename T>
   const absl::flat_hash_map<std::string, size_t> &field_name_to_index();
 
-  template <typename T>
-  Result<std::shared_ptr<TypeInfo>, Error> get_struct_type_info();
-
-  uint32_t get_type_id(const TypeInfo &info) const;
-
-  template <typename T> Result<uint32_t, Error> get_type_id();
-
   /// Get type info by type ID (for non-namespaced types)
-  std::shared_ptr<TypeInfo> get_type_info_by_id(uint32_t type_id) const;
+  /// @return const pointer to TypeInfo if found, error otherwise
+  Result<const TypeInfo *, Error> get_type_info_by_id(uint32_t type_id) const;
 
   /// Get type info by namespace and type name (for namespaced types)
-  std::shared_ptr<TypeInfo>
+  /// @return const pointer to TypeInfo if found, error otherwise
+  Result<const TypeInfo *, Error>
   get_type_info_by_name(const std::string &ns,
                         const std::string &type_name) const;
 
-  /// Read type information dynamically from ReadContext based on type ID.
-  ///
-  /// This method handles reading type info for various type categories:
-  /// - COMPATIBLE_STRUCT/NAMED_COMPATIBLE_STRUCT: reads meta index
-  /// - NAMED_ENUM/NAMED_STRUCT/NAMED_EXT: reads namespace and type name (if 
not
-  /// sharing meta)
-  /// - Other types: looks up by type ID
-  ///
-  /// @return TypeInfo pointer if found, error otherwise
-  Result<std::shared_ptr<TypeInfo>, Error> read_any_typeinfo(ReadContext &ctx);
-
-  /// Read type info from stream with explicit local TypeMeta for field_id
-  /// assignment
-  Result<std::shared_ptr<TypeInfo>, Error>
-  read_any_typeinfo(ReadContext &ctx, const TypeMeta *local_type_meta);
-
   /// Get TypeInfo by type_index (used for looking up registered types)
   /// @return const pointer to TypeInfo if found, error otherwise
   Result<const TypeInfo *, Error>
@@ -479,8 +458,8 @@ public:
 
   /// Get TypeInfo by compile-time type ID (fast path for template types)
   /// Works for enums, structs, and any registered type.
-  /// @return shared_ptr to TypeInfo if found, nullptr otherwise
-  template <typename T> std::shared_ptr<TypeInfo> get_type_info() const;
+  /// @return const pointer to TypeInfo if found, error otherwise
+  template <typename T> Result<const TypeInfo *, Error> get_type_info() const;
 
   /// Builds the final TypeResolver by completing all partial type infos
   /// created during registration.
@@ -497,15 +476,16 @@ public:
   ///
   /// @return A new TypeResolver with all type infos fully initialized and 
ready
   /// for use.
-  Result<std::shared_ptr<TypeResolver>, Error> build_final_type_resolver();
+  Result<std::unique_ptr<TypeResolver>, Error> build_final_type_resolver();
 
-  /// Clones the TypeResolver for use in a new context.
+  /// Deep clones the TypeResolver for use in a new context.
   ///
-  /// This method creates a shallow clone of the TypeResolver. The clone shares
-  /// the same TypeInfo objects as the original but is a separate instance.
+  /// This method creates a deep clone of the TypeResolver. All TypeInfo
+  /// objects are cloned into new instances owned by the new TypeResolver.
+  /// This ensures thread safety when cloning for use in different contexts.
   ///
-  /// @return A new TypeResolver instance
-  std::shared_ptr<TypeResolver> clone() const;
+  /// @return A new TypeResolver instance with cloned TypeInfo objects
+  std::unique_ptr<TypeResolver> clone() const;
 
 private:
   friend class BaseFory;
@@ -525,17 +505,17 @@ private:
                                                 const std::string &type_name);
 
   template <typename T>
-  static Result<std::shared_ptr<TypeInfo>, Error>
+  static Result<std::unique_ptr<TypeInfo>, Error>
   build_struct_type_info(uint32_t type_id, std::string ns,
                          std::string type_name, bool register_by_name);
 
   template <typename T>
-  static Result<std::shared_ptr<TypeInfo>, Error>
+  static Result<std::unique_ptr<TypeInfo>, Error>
   build_enum_type_info(uint32_t type_id, std::string ns, std::string type_name,
                        bool register_by_name);
 
   template <typename T>
-  static Result<std::shared_ptr<TypeInfo>, Error>
+  static Result<std::unique_ptr<TypeInfo>, Error>
   build_ext_type_info(uint32_t type_id, std::string ns, std::string type_name,
                       bool register_by_name);
 
@@ -573,13 +553,14 @@ private:
   static std::string make_name_key(const std::string &ns,
                                    const std::string &name);
 
-  Result<void, Error> register_type_internal(uint64_t ctid,
-                                             std::shared_ptr<TypeInfo> info);
+  /// Register a TypeInfo, taking ownership and storing in primary storage.
+  /// Returns pointer to the stored TypeInfo (owned by TypeResolver).
+  Result<TypeInfo *, Error>
+  register_type_internal(uint64_t ctid, std::unique_ptr<TypeInfo> info);
 
   // For runtime polymorphic lookups (smart pointers with dynamic types)
-  Result<void, Error>
-  register_type_internal_runtime(const std::type_index &type_index,
-                                 std::shared_ptr<TypeInfo> info);
+  void register_type_internal_runtime(const std::type_index &type_index,
+                                      TypeInfo *info);
 
   void check_registration_thread();
 
@@ -593,17 +574,18 @@ private:
   std::thread::id registration_thread_id_;
   bool finalized_;
 
-  // Primary map using compile-time type ID (uint64_t) - fast for template
-  // lookups
-  absl::flat_hash_map<uint64_t, std::shared_ptr<TypeInfo>> type_info_by_ctid_;
-  absl::flat_hash_map<uint32_t, std::shared_ptr<TypeInfo>> type_info_by_id_;
-  absl::flat_hash_map<std::string, std::shared_ptr<TypeInfo>>
-      type_info_by_name_;
-  absl::flat_hash_map<uint64_t, std::shared_ptr<TypeInfo>> partial_type_infos_;
+  // Primary storage - owns all TypeInfo objects
+  std::vector<std::unique_ptr<TypeInfo>> type_infos_;
+
+  // Lookup maps - store raw pointers (borrowed from primary storage)
+  // Using compile-time type ID (uint64_t) - fast for template lookups
+  absl::flat_hash_map<uint64_t, TypeInfo *> type_info_by_ctid_;
+  absl::flat_hash_map<uint32_t, TypeInfo *> type_info_by_id_;
+  absl::flat_hash_map<std::string, TypeInfo *> type_info_by_name_;
+  absl::flat_hash_map<uint64_t, TypeInfo *> partial_type_infos_;
 
   // For runtime polymorphic lookups (smart pointers) - uses std::type_index
-  absl::flat_hash_map<std::type_index, std::shared_ptr<TypeInfo>>
-      type_info_by_runtime_type_;
+  absl::flat_hash_map<std::type_index, TypeInfo *> type_info_by_runtime_type_;
 };
 
 // Alias for backward compatibility (already defined above as top-level)
@@ -671,59 +653,14 @@ TypeResolver::field_name_to_index() {
 }
 
 template <typename T>
-Result<std::shared_ptr<TypeInfo>, Error> TypeResolver::get_struct_type_info() {
-  static_assert(is_fory_serializable_v<T>,
-                "get_struct_type_info requires FORY_STRUCT types");
+Result<const TypeInfo *, Error> TypeResolver::get_type_info() const {
   // Use compile-time type ID (uint64_t key) for fast lookup
   constexpr uint64_t ctid = type_index<T>();
   auto it = type_info_by_ctid_.find(ctid);
   if (FORY_PREDICT_TRUE(it != type_info_by_ctid_.end())) {
     return it->second;
   }
-  return Unexpected(Error::type_error(
-      "Type not registered. All struct/enum/ext types must be explicitly "
-      "registered before serialization."));
-}
-
-template <typename T>
-std::shared_ptr<TypeInfo> TypeResolver::get_type_info() const {
-  // Use compile-time type ID (uint64_t key) for fast lookup
-  constexpr uint64_t ctid = type_index<T>();
-  auto it = type_info_by_ctid_.find(ctid);
-  if (FORY_PREDICT_TRUE(it != type_info_by_ctid_.end())) {
-    return it->second;
-  }
-  return nullptr;
-}
-
-inline uint32_t TypeResolver::get_type_id(const TypeInfo &info) const {
-  // In the xlang spec the numeric type id used on the wire for
-  // structs is the "actual" type id computed by the resolver
-  // (see Rust's `struct_::actual_type_id`). That value is already
-  // stored in `info.type_id` for both id-based and name-based
-  // registrations. Unlike the previous implementation we must not
-  // apply another layer of shifting/tagging here, otherwise the
-  // local type id will no longer match the id written by Java/Rust
-  // and struct reads will fail with `TypeMismatch`.
-  //
-  // Rust equivalent:
-  //   let type_id = T::fory_get_type_id(type_resolver)?;
-  //   context.writer.write_varuint32(type_id);
-  // and on read:
-  //   ensure!(local_type_id == remote_type_id, Error::type_mismatch(...));
-  //
-  // So we simply return the stored actual type id.
-  (void)this; // suppress unused warning in some builds
-  return info.type_id;
-}
-
-template <typename T> Result<uint32_t, Error> TypeResolver::get_type_id() {
-  FORY_TRY(info, get_struct_type_info<T>());
-  if (!info->type_meta) {
-    return Unexpected(Error::type_error(
-        "Type metadata not initialized for requested struct"));
-  }
-  return get_type_id(*info);
+  return Unexpected(Error::type_error("Type not registered"));
 }
 
 template <typename T>
@@ -749,10 +686,12 @@ Result<void, Error> TypeResolver::register_by_id(uint32_t 
type_id) {
           Error::invalid("Harness for registered type is incomplete"));
     }
 
-    partial_type_infos_[ctid] = info;
-    // Also register for runtime polymorphic lookups (smart pointers)
-    type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-    return register_type_internal(ctid, std::move(info));
+    // Register and get back the stored pointer
+    FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+    // Also register for runtime polymorphic lookups and partial type infos
+    partial_type_infos_[ctid] = stored_ptr;
+    register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+    return Result<void, Error>();
   } else if constexpr (std::is_enum_v<T>) {
     uint32_t actual_type_id =
         (type_id << 8) + static_cast<uint32_t>(TypeId::ENUM);
@@ -763,9 +702,10 @@ Result<void, Error> TypeResolver::register_by_id(uint32_t 
type_id) {
           Error::invalid("Harness for registered enum type is incomplete"));
     }
 
-    partial_type_infos_[ctid] = info;
-    type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-    return register_type_internal(ctid, std::move(info));
+    FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+    partial_type_infos_[ctid] = stored_ptr;
+    register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+    return Result<void, Error>();
   } else {
     static_assert(is_fory_serializable_v<T>,
                   "register_by_id requires a type declared with FORY_STRUCT "
@@ -798,9 +738,10 @@ TypeResolver::register_by_name(const std::string &ns,
           Error::invalid("Harness for registered type is incomplete"));
     }
 
-    partial_type_infos_[ctid] = info;
-    type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-    return register_type_internal(ctid, std::move(info));
+    FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+    partial_type_infos_[ctid] = stored_ptr;
+    register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+    return Result<void, Error>();
   } else if constexpr (std::is_enum_v<T>) {
     uint32_t actual_type_id = static_cast<uint32_t>(TypeId::NAMED_ENUM);
     FORY_TRY(info,
@@ -810,9 +751,10 @@ TypeResolver::register_by_name(const std::string &ns,
           Error::invalid("Harness for registered enum type is incomplete"));
     }
 
-    partial_type_infos_[ctid] = info;
-    type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-    return register_type_internal(ctid, std::move(info));
+    FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+    partial_type_infos_[ctid] = stored_ptr;
+    register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+    return Result<void, Error>();
   } else {
     static_assert(is_fory_serializable_v<T>,
                   "register_by_name requires a type declared with FORY_STRUCT "
@@ -836,9 +778,10 @@ Result<void, Error> 
TypeResolver::register_ext_type_by_id(uint32_t type_id) {
 
   FORY_TRY(info, build_ext_type_info<T>(actual_type_id, "", "", false));
 
-  partial_type_infos_[ctid] = info;
-  type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-  return register_type_internal(ctid, std::move(info));
+  FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+  partial_type_infos_[ctid] = stored_ptr;
+  register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+  return Result<void, Error>();
 }
 
 template <typename T>
@@ -856,13 +799,14 @@ TypeResolver::register_ext_type_by_name(const std::string 
&ns,
   uint32_t actual_type_id = static_cast<uint32_t>(TypeId::NAMED_EXT);
   FORY_TRY(info, build_ext_type_info<T>(actual_type_id, ns, type_name, true));
 
-  partial_type_infos_[ctid] = info;
-  type_info_by_runtime_type_[std::type_index(typeid(T))] = info;
-  return register_type_internal(ctid, std::move(info));
+  FORY_TRY(stored_ptr, register_type_internal(ctid, std::move(info)));
+  partial_type_infos_[ctid] = stored_ptr;
+  register_type_internal_runtime(std::type_index(typeid(T)), stored_ptr);
+  return Result<void, Error>();
 }
 
 template <typename T>
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<std::unique_ptr<TypeInfo>, Error>
 TypeResolver::build_struct_type_info(uint32_t type_id, std::string ns,
                                      std::string type_name,
                                      bool register_by_name) {
@@ -873,7 +817,7 @@ TypeResolver::build_struct_type_info(uint32_t type_id, 
std::string ns,
     type_id = static_cast<uint32_t>(TypeId::STRUCT);
   }
 
-  auto entry = std::make_shared<TypeInfo>();
+  auto entry = std::make_unique<TypeInfo>();
   entry->type_id = type_id;
   entry->namespace_name = std::move(ns);
   entry->register_by_name = register_by_name;
@@ -936,7 +880,7 @@ TypeResolver::build_struct_type_info(uint32_t type_id, 
std::string ns,
 }
 
 template <typename T>
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<std::unique_ptr<TypeInfo>, Error>
 TypeResolver::build_enum_type_info(uint32_t type_id, std::string ns,
                                    std::string type_name,
                                    bool register_by_name) {
@@ -947,7 +891,7 @@ TypeResolver::build_enum_type_info(uint32_t type_id, 
std::string ns,
         "type_name must be non-empty when register_by_name is true"));
   }
 
-  auto entry = std::make_shared<TypeInfo>();
+  auto entry = std::make_unique<TypeInfo>();
   entry->type_id = type_id;
   entry->namespace_name = std::move(ns);
   entry->register_by_name = register_by_name;
@@ -979,11 +923,11 @@ TypeResolver::build_enum_type_info(uint32_t type_id, 
std::string ns,
 }
 
 template <typename T>
-Result<std::shared_ptr<TypeInfo>, Error>
+Result<std::unique_ptr<TypeInfo>, Error>
 TypeResolver::build_ext_type_info(uint32_t type_id, std::string ns,
                                   std::string type_name,
                                   bool register_by_name) {
-  auto entry = std::make_shared<TypeInfo>();
+  auto entry = std::make_unique<TypeInfo>();
   entry->type_id = type_id;
   entry->namespace_name = ns;
   entry->type_name = type_name;
@@ -1094,57 +1038,61 @@ inline std::string TypeResolver::make_name_key(const 
std::string &ns,
   return key;
 }
 
-inline Result<void, Error>
+inline Result<TypeInfo *, Error>
 TypeResolver::register_type_internal(uint64_t ctid,
-                                     std::shared_ptr<TypeInfo> info) {
+                                     std::unique_ptr<TypeInfo> info) {
   if (!info || !info->harness.valid()) {
     return Unexpected(
         Error::invalid("TypeInfo or harness is invalid during registration"));
   }
 
-  type_info_by_ctid_[ctid] = info;
+  // Store in primary storage and get raw pointer
+  TypeInfo *raw_ptr = info.get();
+  type_infos_.push_back(std::move(info));
+
+  type_info_by_ctid_[ctid] = raw_ptr;
 
-  if (info->type_id != 0 && !info->register_by_name) {
-    auto it = type_info_by_id_.find(info->type_id);
-    if (it != type_info_by_id_.end() && it->second.get() != info.get()) {
+  if (raw_ptr->type_id != 0 && !raw_ptr->register_by_name) {
+    auto it = type_info_by_id_.find(raw_ptr->type_id);
+    if (it != type_info_by_id_.end() && it->second != raw_ptr) {
       return Unexpected(Error::invalid("Type id already registered: " +
-                                       std::to_string(info->type_id)));
+                                       std::to_string(raw_ptr->type_id)));
     }
-    type_info_by_id_[info->type_id] = info;
+    type_info_by_id_[raw_ptr->type_id] = raw_ptr;
   }
 
-  if (info->register_by_name) {
-    auto key = make_name_key(info->namespace_name, info->type_name);
+  if (raw_ptr->register_by_name) {
+    auto key = make_name_key(raw_ptr->namespace_name, raw_ptr->type_name);
     auto it = type_info_by_name_.find(key);
-    if (it != type_info_by_name_.end() && it->second.get() != info.get()) {
+    if (it != type_info_by_name_.end() && it->second != raw_ptr) {
       return Unexpected(Error::invalid(
-          "Type already registered for namespace '" + info->namespace_name +
-          "' and name '" + info->type_name + "'"));
+          "Type already registered for namespace '" + raw_ptr->namespace_name +
+          "' and name '" + raw_ptr->type_name + "'"));
     }
-    type_info_by_name_[key] = info;
+    type_info_by_name_[key] = raw_ptr;
   }
 
-  return Result<void, Error>();
+  return raw_ptr;
 }
 
-inline Result<void, Error>
+inline void
 TypeResolver::register_type_internal_runtime(const std::type_index &type_index,
-                                             std::shared_ptr<TypeInfo> info) {
+                                             TypeInfo *info) {
   // For runtime polymorphic lookups (smart pointers)
   type_info_by_runtime_type_[type_index] = info;
-  return Result<void, Error>();
 }
 
-inline std::shared_ptr<TypeInfo>
+inline Result<const TypeInfo *, Error>
 TypeResolver::get_type_info_by_id(uint32_t type_id) const {
   auto it = type_info_by_id_.find(type_id);
   if (it != type_info_by_id_.end()) {
     return it->second;
   }
-  return nullptr;
+  return Unexpected(Error::type_error("TypeInfo not found for type_id: " +
+                                      std::to_string(type_id)));
 }
 
-inline std::shared_ptr<TypeInfo>
+inline Result<const TypeInfo *, Error>
 TypeResolver::get_type_info_by_name(const std::string &ns,
                                     const std::string &type_name) const {
   auto key = make_name_key(ns, type_name);
@@ -1152,7 +1100,8 @@ TypeResolver::get_type_info_by_name(const std::string &ns,
   if (it != type_info_by_name_.end()) {
     return it->second;
   }
-  return nullptr;
+  return Unexpected(Error::type_error("TypeInfo not found for type: " + ns +
+                                      "." + type_name));
 }
 
 // ============================================================================
@@ -1161,13 +1110,8 @@ TypeResolver::get_type_info_by_name(const std::string 
&ns,
 // ============================================================================
 
 template <typename E> Result<void, Error> WriteContext::write_enum_typeinfo() {
-  auto type_info = type_resolver_->get_type_info<E>();
-  if (type_info) {
-    return write_enum_typeinfo(type_info.get());
-  }
-  // Enum not registered, write plain ENUM type id
-  buffer_.WriteVarUint32(static_cast<uint32_t>(TypeId::ENUM));
-  return Result<void, Error>();
+  FORY_TRY(type_info, type_resolver_->get_type_info<E>());
+  return write_enum_typeinfo(type_info);
 }
 
 } // namespace serialization


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to