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 8181e1470 perf(c++): directly error set instead of result to reduce 
cost on hotpath (#2959)
8181e1470 is described below

commit 8181e147009122205143c72348387bda10d928fd
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Dec 2 12:04:57 2025 +0800

    perf(c++): directly error set instead of result to reduce cost on hotpath 
(#2959)
    
    ## What does this PR do?
    
    directly error set instead of result to reduce cost on hotpath for
    buffer read, so we can remove `Result` cost.
    ## Related issues
    
    #2958
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    Before this PR:
    
    | Datatype | Operation | Fory (ns) | Protobuf (ns) | Faster |
    |----------|-----------|-----------|---------------|--------|
    | Struct | Serialize | 10.7 | 19.8 | Fory (1.9x) |
    | Struct | Deserialize | 53.7 | 16.2 | Protobuf (3.3x) |
    
    With this PR:
    
    | Datatype | Operation | Fory (ns) | Protobuf (ns) | Faster |
    |----------|-----------|-----------|---------------|--------|
    | Struct | Serialize | 10.3 | 19.8 | Fory (1.9x) |
    | Struct | Deserialize | 33.6 | 16.5 | Protobuf (2.0x) |
---
 cpp/fory/meta/meta_string.cc                   |  26 ++-
 cpp/fory/row/schema.cc                         |  53 +++--
 cpp/fory/serialization/array_serializer.h      |  34 ++-
 cpp/fory/serialization/basic_serializer.h      | 308 ++++++++++++++++++++-----
 cpp/fory/serialization/collection_serializer.h | 119 +++++++---
 cpp/fory/serialization/context.cc              |  27 ++-
 cpp/fory/serialization/context.h               | 101 ++++++--
 cpp/fory/serialization/enum_serializer.h       |  12 +-
 cpp/fory/serialization/fory.h                  |   8 +-
 cpp/fory/serialization/map_serializer.h        |  60 ++++-
 cpp/fory/serialization/serializer.h            |  48 +++-
 cpp/fory/serialization/skip.cc                 | 120 ++++++++--
 cpp/fory/serialization/smart_ptr_serializers.h |  46 +++-
 cpp/fory/serialization/struct_serializer.h     | 132 ++++++++++-
 cpp/fory/serialization/temporal_serializers.h  |  36 ++-
 cpp/fory/serialization/type_resolver.cc        |  99 ++++++--
 cpp/fory/serialization/xlang_test_main.cc      | 119 +++++-----
 cpp/fory/util/buffer.h                         | 171 ++++++++------
 cpp/fory/util/error.h                          | 106 +++++++--
 cpp/fory/util/error_test.cc                    |   6 +-
 20 files changed, 1238 insertions(+), 393 deletions(-)

diff --git a/cpp/fory/meta/meta_string.cc b/cpp/fory/meta/meta_string.cc
index 2aa8012a8..a9fa6ec69 100644
--- a/cpp/fory/meta/meta_string.cc
+++ b/cpp/fory/meta/meta_string.cc
@@ -238,9 +238,13 @@ MetaStringTable::MetaStringTable() = default;
 
 Result<std::string, Error>
 MetaStringTable::read_string(Buffer &buffer, const MetaStringDecoder &decoder) 
{
+  Error error;
   // Header is encoded with VarUint32Small7 on Java side, but wire
   // format is still standard varuint32.
-  FORY_TRY(header, buffer.ReadVarUint32());
+  uint32_t header = buffer.ReadVarUint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   uint32_t len_or_id = header >> 1;
   bool is_ref = (header & 0x1u) != 0;
 
@@ -264,16 +268,25 @@ MetaStringTable::read_string(Buffer &buffer, const 
MetaStringDecoder &decoder) {
     // The original encoding is not transmitted explicitly. For cross-language
     // purposes we treat the payload bytes as UTF8 and let callers handle any
     // higher-level semantics.
-    FORY_TRY(hash_code, buffer.ReadInt64());
+    int64_t hash_code = buffer.ReadInt64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     (void)hash_code; // hash_code is only used for Java-side caching.
     bytes.resize(len);
     if (len > 0) {
-      FORY_RETURN_NOT_OK(buffer.ReadBytes(bytes.data(), len));
+      buffer.ReadBytes(bytes.data(), len, &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
     }
     encoding = MetaEncoding::UTF8;
   } else {
     // Small string layout: encoding(byte) + data[len]
-    FORY_TRY(enc_byte_res, buffer.ReadInt8());
+    int8_t enc_byte_res = buffer.ReadInt8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     uint8_t enc_byte = static_cast<uint8_t>(enc_byte_res);
     if (len == 0) {
       if (enc_byte != 0) {
@@ -285,7 +298,10 @@ MetaStringTable::read_string(Buffer &buffer, const 
MetaStringDecoder &decoder) {
       FORY_TRY(enc, ToMetaEncoding(enc_byte));
       encoding = enc;
       bytes.resize(len);
-      FORY_RETURN_NOT_OK(buffer.ReadBytes(bytes.data(), len));
+      buffer.ReadBytes(bytes.data(), len, &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
     }
   }
 
diff --git a/cpp/fory/row/schema.cc b/cpp/fory/row/schema.cc
index 03cca3c1e..d880bdcae 100644
--- a/cpp/fory/row/schema.cc
+++ b/cpp/fory/row/schema.cc
@@ -91,7 +91,11 @@ Result<void, Error> WriteField(Buffer &buffer, const Field 
&field) {
 }
 
 Result<FieldPtr, Error> ReadField(Buffer &buffer) {
-  FORY_TRY(header_byte, buffer.ReadUint8());
+  Error error;
+  uint8_t header_byte = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   int header = header_byte;
   int encoding_index = header & 0x03;
   int name_size_minus1 = (header >> 2) & 0x0F;
@@ -99,15 +103,20 @@ Result<FieldPtr, Error> ReadField(Buffer &buffer) {
 
   size_t name_size;
   if (name_size_minus1 == FIELD_NAME_SIZE_THRESHOLD) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     name_size = extra + FIELD_NAME_SIZE_THRESHOLD;
   } else {
     name_size = name_size_minus1 + 1;
   }
 
   std::vector<uint8_t> name_bytes(name_size);
-  FORY_RETURN_NOT_OK(
-      buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size)));
+  buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size), 
&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   MetaEncoding encoding = FIELD_NAME_ENCODINGS[encoding_index];
   auto decode_result =
@@ -143,7 +152,11 @@ Result<void, Error> WriteType(Buffer &buffer, const 
DataType &type) {
 }
 
 Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
-  FORY_TRY(type_id_byte, buffer.ReadUint8());
+  Error error;
+  uint8_t type_id_byte = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   TypeId type_id = static_cast<TypeId>(type_id_byte);
 
   switch (type_id) {
@@ -174,8 +187,14 @@ Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
   case TypeId::LOCAL_DATE:
     return date32();
   case TypeId::DECIMAL: {
-    FORY_TRY(precision, buffer.ReadUint8());
-    FORY_TRY(scale, buffer.ReadUint8());
+    uint8_t precision = buffer.ReadUint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    uint8_t scale = buffer.ReadUint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return decimal(precision, scale);
   }
   case TypeId::LIST: {
@@ -190,7 +209,10 @@ Result<DataTypePtr, Error> ReadType(Buffer &buffer) {
         std::make_shared<MapType>(key_field->type(), item_field->type()));
   }
   case TypeId::STRUCT: {
-    FORY_TRY(struct_num_fields, buffer.ReadVarUint32());
+    uint32_t struct_num_fields = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     std::vector<FieldPtr> fields;
     fields.reserve(struct_num_fields);
     for (uint32_t i = 0; i < struct_num_fields; ++i) {
@@ -234,22 +256,21 @@ SchemaPtr Schema::FromBytes(const std::vector<uint8_t> 
&bytes) {
 }
 
 SchemaPtr Schema::FromBytes(Buffer &buffer) {
-  auto version_result = buffer.ReadUint8();
-  if (!version_result.ok()) {
-    throw std::runtime_error(version_result.error().message());
+  Error error;
+  uint8_t version = buffer.ReadUint8(&error);
+  if (!error.ok()) {
+    throw std::runtime_error(error.message());
   }
-  uint8_t version = version_result.value();
   if (version != SCHEMA_VERSION) {
     throw std::runtime_error(
         "Unsupported schema version: " + std::to_string(version) +
         ", expected: " + std::to_string(SCHEMA_VERSION));
   }
 
-  auto num_fields_result = buffer.ReadVarUint32();
-  if (!num_fields_result.ok()) {
-    throw std::runtime_error(num_fields_result.error().message());
+  uint32_t num_fields = buffer.ReadVarUint32(&error);
+  if (!error.ok()) {
+    throw std::runtime_error(error.message());
   }
-  uint32_t num_fields = num_fields_result.value();
 
   std::vector<FieldPtr> fields;
   fields.reserve(num_fields);
diff --git a/cpp/fory/serialization/array_serializer.h 
b/cpp/fory/serialization/array_serializer.h
index eaa79b4d0..c21c5f78f 100644
--- a/cpp/fory/serialization/array_serializer.h
+++ b/cpp/fory/serialization/array_serializer.h
@@ -93,8 +93,12 @@ struct Serializer<
     if (!has_value) {
       return std::array<T, N>();
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -105,7 +109,11 @@ struct Serializer<
 
   static Result<std::array<T, N>, Error> read_data(ReadContext &ctx) {
     // Read array length
-    FORY_TRY(length, ctx.read_varuint32());
+    Error error;
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     if (length != N) {
       return Unexpected(Error::invalid_data("Array size mismatch: expected " +
@@ -115,7 +123,10 @@ struct Serializer<
 
     std::array<T, N> arr;
     if constexpr (N > 0) {
-      FORY_RETURN_NOT_OK(ctx.read_bytes(arr.data(), N * sizeof(T)));
+      ctx.read_bytes(arr.data(), N * sizeof(T), &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
     }
     return arr;
   }
@@ -161,8 +172,12 @@ template <size_t N> struct Serializer<std::array<bool, N>> 
{
     if (!has_value) {
       return std::array<bool, N>();
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -173,7 +188,11 @@ template <size_t N> struct Serializer<std::array<bool, N>> 
{
 
   static Result<std::array<bool, N>, Error> read_data(ReadContext &ctx) {
     // Read array length
-    FORY_TRY(length, ctx.read_varuint32());
+    Error error;
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (length != N) {
       return Unexpected(Error::invalid_data("Array size mismatch: expected " +
                                             std::to_string(N) + " but got " +
@@ -181,7 +200,10 @@ template <size_t N> struct Serializer<std::array<bool, N>> 
{
     }
     std::array<bool, N> arr;
     for (size_t i = 0; i < N; ++i) {
-      FORY_TRY(byte, ctx.read_uint8());
+      uint8_t byte = ctx.read_uint8(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       arr[i] = (byte != 0);
     }
     return arr;
diff --git a/cpp/fory/serialization/basic_serializer.h 
b/cpp/fory/serialization/basic_serializer.h
index 950e0c11f..efd5e82cd 100644
--- a/cpp/fory/serialization/basic_serializer.h
+++ b/cpp/fory/serialization/basic_serializer.h
@@ -49,7 +49,11 @@ template <> struct Serializer<bool> {
 
   /// Read and validate type info (primitives use read_varuint32 directly)
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -87,20 +91,31 @@ template <> struct Serializer<bool> {
     if (!has_value) {
       return false;
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    FORY_TRY(value, ctx.read_uint8());
+    uint8_t value = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value != 0;
   }
 
   /// Read boolean data only (no type info)
   static inline Result<bool, Error> read_data(ReadContext &ctx) {
-    FORY_TRY(value, ctx.read_uint8());
+    Error error;
+    uint8_t value = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value != 0;
   }
 
@@ -129,7 +144,11 @@ template <> struct Serializer<int8_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -164,18 +183,31 @@ template <> struct Serializer<int8_t> {
     if (!has_value) {
       return static_cast<int8_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    return ctx.read_int8();
+    int8_t value = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int8_t, Error> read_data(ReadContext &ctx) {
-    return ctx.read_int8();
+    Error error;
+    int8_t value = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int8_t, Error> read_data_generic(ReadContext &ctx,
@@ -200,7 +232,11 @@ template <> struct Serializer<int16_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -235,21 +271,30 @@ template <> struct Serializer<int16_t> {
     if (!has_value) {
       return static_cast<int16_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    int16_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(int16_t)));
+    int16_t value = ctx.read_int16(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<int16_t, Error> read_data(ReadContext &ctx) {
-    int16_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(int16_t)));
+    Error error;
+    int16_t value = ctx.read_int16(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -275,7 +320,11 @@ template <> struct Serializer<int32_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -310,18 +359,31 @@ template <> struct Serializer<int32_t> {
     if (!has_value) {
       return static_cast<int32_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    return ctx.read_varint32();
+    int32_t value = ctx.read_varint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int32_t, Error> read_data(ReadContext &ctx) {
-    return ctx.read_varint32();
+    Error error;
+    int32_t value = ctx.read_varint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int32_t, Error> read_data_generic(ReadContext &ctx,
@@ -346,7 +408,11 @@ template <> struct Serializer<int64_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -381,18 +447,31 @@ template <> struct Serializer<int64_t> {
     if (!has_value) {
       return static_cast<int64_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    return ctx.read_varint64();
+    int64_t value = ctx.read_varint64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int64_t, Error> read_data(ReadContext &ctx) {
-    return ctx.read_varint64();
+    Error error;
+    int64_t value = ctx.read_varint64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<int64_t, Error> read_data_generic(ReadContext &ctx,
@@ -417,7 +496,11 @@ template <> struct Serializer<float> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -451,21 +534,30 @@ template <> struct Serializer<float> {
     if (!has_value) {
       return 0.0f;
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    float value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(float)));
+    float value = ctx.read_float(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<float, Error> read_data(ReadContext &ctx) {
-    float value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(float)));
+    Error error;
+    float value = ctx.read_float(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -491,7 +583,11 @@ template <> struct Serializer<double> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -526,21 +622,30 @@ template <> struct Serializer<double> {
     if (!has_value) {
       return 0.0;
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    double value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(double)));
+    double value = ctx.read_double(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<double, Error> read_data(ReadContext &ctx) {
-    double value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(double)));
+    Error error;
+    double value = ctx.read_double(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -570,7 +675,11 @@ template <> struct Serializer<uint8_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -605,18 +714,31 @@ template <> struct Serializer<uint8_t> {
     if (!has_value) {
       return static_cast<uint8_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    return ctx.read_uint8();
+    uint8_t value = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<uint8_t, Error> read_data(ReadContext &ctx) {
-    return ctx.read_uint8();
+    Error error;
+    uint8_t value = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
+    return value;
   }
 
   static inline Result<uint8_t, Error> read_data_generic(ReadContext &ctx,
@@ -641,7 +763,11 @@ template <> struct Serializer<uint16_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -676,21 +802,30 @@ template <> struct Serializer<uint16_t> {
     if (!has_value) {
       return static_cast<uint16_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    uint16_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint16_t)));
+    uint16_t value = ctx.read_uint16(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<uint16_t, Error> read_data(ReadContext &ctx) {
-    uint16_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint16_t)));
+    Error error;
+    uint16_t value = ctx.read_uint16(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -716,7 +851,11 @@ template <> struct Serializer<uint32_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -751,21 +890,30 @@ template <> struct Serializer<uint32_t> {
     if (!has_value) {
       return static_cast<uint32_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    uint32_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint32_t)));
+    uint32_t value = ctx.read_uint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<uint32_t, Error> read_data(ReadContext &ctx) {
-    uint32_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint32_t)));
+    Error error;
+    uint32_t value = ctx.read_uint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -791,7 +939,11 @@ template <> struct Serializer<uint64_t> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -826,21 +978,30 @@ template <> struct Serializer<uint64_t> {
     if (!has_value) {
       return static_cast<uint64_t>(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
-    uint64_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint64_t)));
+    uint64_t value = ctx.read_uint64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
   static inline Result<uint64_t, Error> read_data(ReadContext &ctx) {
-    uint64_t value;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&value, sizeof(uint64_t)));
+    Error error;
+    uint64_t value = ctx.read_uint64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return value;
   }
 
@@ -877,7 +1038,11 @@ template <> struct Serializer<std::string> {
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
-    FORY_TRY(actual, ctx.read_varuint32());
+    Error error;
+    uint32_t actual = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (actual != static_cast<uint32_t>(type_id)) {
       return Unexpected(
           Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
@@ -925,8 +1090,12 @@ template <> struct Serializer<std::string> {
     if (!has_value) {
       return std::string();
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -937,7 +1106,11 @@ template <> struct Serializer<std::string> {
 
   static inline Result<std::string, Error> read_data(ReadContext &ctx) {
     // Read size with encoding using varuint36small
-    FORY_TRY(size_with_encoding, ctx.read_varuint36small());
+    Error error;
+    uint64_t size_with_encoding = ctx.read_varuint36small(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Extract size and encoding from lower 2 bits
     uint64_t length = size_with_encoding >> 2;
@@ -952,7 +1125,10 @@ template <> struct Serializer<std::string> {
     switch (encoding) {
     case StringEncoding::LATIN1: {
       std::vector<uint8_t> bytes(length);
-      FORY_RETURN_NOT_OK(ctx.read_bytes(bytes.data(), length));
+      ctx.read_bytes(bytes.data(), length, &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return latin1ToUtf8(bytes.data(), length);
     }
     case StringEncoding::UTF16: {
@@ -960,14 +1136,20 @@ template <> struct Serializer<std::string> {
         return Unexpected(Error::invalid_data("UTF-16 length must be even"));
       }
       std::vector<uint16_t> utf16_chars(length / 2);
-      FORY_RETURN_NOT_OK(ctx.read_bytes(
-          reinterpret_cast<uint8_t *>(utf16_chars.data()), length));
+      ctx.read_bytes(reinterpret_cast<uint8_t *>(utf16_chars.data()), length,
+                     &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return utf16ToUtf8(utf16_chars.data(), utf16_chars.size());
     }
     case StringEncoding::UTF8: {
       // UTF-8: read bytes directly
       std::string result(length, '\0');
-      FORY_RETURN_NOT_OK(ctx.read_bytes(&result[0], length));
+      ctx.read_bytes(&result[0], length, &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return result;
     }
     default:
diff --git a/cpp/fory/serialization/collection_serializer.h 
b/cpp/fory/serialization/collection_serializer.h
index 139646e8e..4c02d0e94 100644
--- a/cpp/fory/serialization/collection_serializer.h
+++ b/cpp/fory/serialization/collection_serializer.h
@@ -133,7 +133,7 @@ struct Serializer<
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   static inline Result<void, Error> write(const std::vector<T, Alloc> &vec,
@@ -174,8 +174,12 @@ struct Serializer<
       return std::vector<T, Alloc>();
     }
 
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -193,7 +197,11 @@ struct Serializer<
 
   static inline Result<std::vector<T, Alloc>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(total_bytes_u32, ctx.read_varuint32());
+    Error error;
+    uint32_t total_bytes_u32 = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (sizeof(T) == 0) {
       return std::vector<T, Alloc>();
     }
@@ -204,8 +212,11 @@ struct Serializer<
     size_t elem_count = total_bytes_u32 / sizeof(T);
     std::vector<T, Alloc> result(elem_count);
     if (total_bytes_u32 > 0) {
-      FORY_RETURN_NOT_OK(ctx.read_bytes(
-          result.data(), static_cast<uint32_t>(total_bytes_u32)));
+      ctx.read_bytes(result.data(), static_cast<uint32_t>(total_bytes_u32),
+                     &error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
     }
     return result;
   }
@@ -229,7 +240,7 @@ struct Serializer<
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   static Result<std::vector<T, Alloc>, Error>
@@ -240,9 +251,13 @@ struct Serializer<
       return std::vector<T, Alloc>();
     }
 
+    Error error;
     // Optional type info for polymorphic containers
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       uint32_t low = type_id_read & 0xffu;
       if (low != static_cast<uint32_t>(type_id)) {
         return Unexpected(
@@ -251,7 +266,10 @@ struct Serializer<
     }
 
     // Length written via writeVarUint32Small7
-    FORY_TRY(length, ctx.read_varuint32());
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     // Per xlang spec: header and type_info are omitted when length is 0
     // This matches Rust's collection.rs behavior
     if (length == 0) {
@@ -259,8 +277,10 @@ struct Serializer<
     }
 
     // Elements header bitmap (CollectionFlags)
-    FORY_TRY(bitmap_u8, ctx.read_uint8());
-    uint8_t bitmap = bitmap_u8;
+    uint8_t bitmap = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     bool track_ref = (bitmap & 0x1u) != 0;
     bool has_null = (bitmap & 0x2u) != 0;
     bool is_decl_type = (bitmap & 0x4u) != 0;
@@ -479,7 +499,11 @@ struct Serializer<
 
   static inline Result<std::vector<T, Alloc>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(size, ctx.read_varuint32());
+    Error error;
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     std::vector<T, Alloc> result;
     result.reserve(size);
     for (uint32_t i = 0; i < size; ++i) {
@@ -505,7 +529,7 @@ template <typename Alloc> struct 
Serializer<std::vector<bool, Alloc>> {
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   // Match Rust signature: fory_write(&self, context, write_ref_info,
@@ -545,8 +569,12 @@ template <typename Alloc> struct 
Serializer<std::vector<bool, Alloc>> {
       return std::vector<bool, Alloc>();
     }
 
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -557,7 +585,11 @@ template <typename Alloc> struct 
Serializer<std::vector<bool, Alloc>> {
 
   static inline Result<std::vector<bool, Alloc>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(size, ctx.read_varuint32());
+    Error error;
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     std::vector<bool, Alloc> result(size);
     // Fast path: bulk read all bytes at once if we have enough buffer
     Buffer &buffer = ctx.buffer();
@@ -570,7 +602,10 @@ template <typename Alloc> struct 
Serializer<std::vector<bool, Alloc>> {
     } else {
       // Fallback: read byte-by-byte with bounds checking
       for (uint32_t i = 0; i < size; ++i) {
-        FORY_TRY(byte, ctx.read_uint8());
+        uint8_t byte = ctx.read_uint8(&error);
+        if (FORY_PREDICT_FALSE(!error.ok())) {
+          return Unexpected(std::move(error));
+        }
         result[i] = (byte != 0);
       }
     }
@@ -588,7 +623,7 @@ struct Serializer<std::set<T, Args...>> {
 
   static inline Result<void, Error> write_type_info(WriteContext &ctx) {
     ctx.write_varuint32(static_cast<uint32_t>(type_id));
-    return Result<void, Error>();
+    return {};
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
@@ -597,7 +632,7 @@ struct Serializer<std::set<T, Args...>> {
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   // Match Rust signature: fory_write(&self, context, write_ref_info,
@@ -686,9 +721,13 @@ struct Serializer<std::set<T, Args...>> {
       return std::set<T, Args...>();
     }
 
+    Error error;
     // Read type info
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -696,15 +735,20 @@ struct Serializer<std::set<T, Args...>> {
     }
 
     // Read set size
-    FORY_TRY(size, ctx.read_varuint32());
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     // Per xlang spec: header and type_info are omitted when length is 0
     if (size == 0) {
       return std::set<T, Args...>();
     }
 
     // Read elements header bitmap (CollectionFlags) in xlang mode
-    FORY_TRY(bitmap_u8, ctx.read_uint8());
-    uint8_t bitmap = bitmap_u8;
+    uint8_t bitmap = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     bool track_ref = (bitmap & 0x1u) != 0;
     bool has_null = (bitmap & 0x2u) != 0;
     bool is_decl_type = (bitmap & 0x4u) != 0;
@@ -760,7 +804,11 @@ struct Serializer<std::set<T, Args...>> {
 
   static inline Result<std::set<T, Args...>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(size, ctx.read_varuint32());
+    Error error;
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     std::set<T, Args...> result;
     for (uint32_t i = 0; i < size; ++i) {
       FORY_TRY(elem, Serializer<T>::read_data(ctx));
@@ -780,7 +828,7 @@ struct Serializer<std::unordered_set<T, Args...>> {
 
   static inline Result<void, Error> write_type_info(WriteContext &ctx) {
     ctx.write_varuint32(static_cast<uint32_t>(type_id));
-    return Result<void, Error>();
+    return {};
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
@@ -789,7 +837,7 @@ struct Serializer<std::unordered_set<T, Args...>> {
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   // Match Rust signature: fory_write(&self, context, write_ref_info,
@@ -877,9 +925,13 @@ struct Serializer<std::unordered_set<T, Args...>> {
       return std::unordered_set<T, Args...>();
     }
 
+    Error error;
     // Read type info
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -887,7 +939,10 @@ struct Serializer<std::unordered_set<T, Args...>> {
     }
 
     // Read set size
-    FORY_TRY(size, ctx.read_varuint32());
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Per xlang spec: header and type_info are omitted when length is 0
     if (size == 0) {
@@ -895,8 +950,10 @@ struct Serializer<std::unordered_set<T, Args...>> {
     }
 
     // Read elements header bitmap (CollectionFlags) in xlang mode
-    FORY_TRY(bitmap_u8, ctx.read_uint8());
-    uint8_t bitmap = bitmap_u8;
+    uint8_t bitmap = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     bool track_ref = (bitmap & 0x1u) != 0;
     bool has_null = (bitmap & 0x2u) != 0;
     bool is_decl_type = (bitmap & 0x4u) != 0;
@@ -954,7 +1011,11 @@ struct Serializer<std::unordered_set<T, Args...>> {
 
   static inline Result<std::unordered_set<T, Args...>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(size, ctx.read_varuint32());
+    Error error;
+    uint32_t size = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     std::unordered_set<T, Args...> result;
     result.reserve(size);
     for (uint32_t i = 0; i < size; ++i) {
diff --git a/cpp/fory/serialization/context.cc 
b/cpp/fory/serialization/context.cc
index 9a43001ee..81797293d 100644
--- a/cpp/fory/serialization/context.cc
+++ b/cpp/fory/serialization/context.cc
@@ -389,12 +389,19 @@ Result<size_t, Error> ReadContext::load_type_meta(int32_t 
meta_offset) {
   buffer_->ReaderIndex(static_cast<uint32_t>(meta_start));
 
   // Load all TypeMetas
-  FORY_TRY(meta_size, buffer_->ReadVarUint32());
+  Error error;
+  uint32_t meta_size = buffer_->ReadVarUint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   reading_type_infos_.reserve(meta_size);
 
   for (uint32_t i = 0; i < meta_size; i++) {
     // Read the 8-byte header first for caching
-    FORY_TRY(meta_header, buffer_->ReadInt64());
+    int64_t meta_header = buffer_->ReadInt64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Check if we already parsed this type meta (cache lookup by header)
     auto cache_it = parsed_type_infos_.find(meta_header);
@@ -483,21 +490,31 @@ ReadContext::get_type_info_by_index(size_t index) const {
 }
 
 Result<const TypeInfo *, Error> ReadContext::read_any_typeinfo() {
-  FORY_TRY(type_id, buffer_->ReadVarUint32());
+  Error error;
+  uint32_t type_id = buffer_->ReadVarUint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   uint32_t type_id_low = type_id & 0xff;
 
   // Mirror Rust's read_any_typeinfo using switch for jump table generation
   switch (type_id_low) {
   case static_cast<uint32_t>(TypeId::NAMED_COMPATIBLE_STRUCT):
   case static_cast<uint32_t>(TypeId::COMPATIBLE_STRUCT): {
-    FORY_TRY(meta_index, buffer_->ReadVarUint32());
+    uint32_t meta_index = buffer_->ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return get_type_info_by_index(meta_index);
   }
   case static_cast<uint32_t>(TypeId::NAMED_ENUM):
   case static_cast<uint32_t>(TypeId::NAMED_EXT):
   case static_cast<uint32_t>(TypeId::NAMED_STRUCT): {
     if (config_->compatible) {
-      FORY_TRY(meta_index, buffer_->ReadVarUint32());
+      uint32_t meta_index = buffer_->ReadVarUint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return get_type_info_by_index(meta_index);
     }
     FORY_TRY(namespace_str,
diff --git a/cpp/fory/serialization/context.h b/cpp/fory/serialization/context.h
index 9a3d0ddd1..b7a7ba582 100644
--- a/cpp/fory/serialization/context.h
+++ b/cpp/fory/serialization/context.h
@@ -368,41 +368,96 @@ public:
     }
   }
 
-  /// Read uint8_t value from buffer.
-  inline Result<uint8_t, Error> read_uint8() { return buffer().ReadUint8(); }
+  // 
===========================================================================
+  // Read methods with Error* parameter
+  // All methods accept Error* as parameter for reduced overhead.
+  // On success, error->ok() remains true. On failure, error is set.
+  // 
===========================================================================
+
+  /// Read uint8_t value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE uint8_t read_uint8(Error *error) {
+    return buffer().ReadUint8(error);
+  }
 
-  /// Read int8_t value from buffer.
-  inline Result<int8_t, Error> read_int8() { return buffer().ReadInt8(); }
+  /// Read int8_t value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE int8_t read_int8(Error *error) {
+    return buffer().ReadInt8(error);
+  }
 
-  /// Read uint32_t value as varint from buffer.
-  inline Result<uint32_t, Error> read_varuint32() {
-    return buffer().ReadVarUint32();
+  /// Read uint16_t value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE uint16_t read_uint16(Error *error) {
+    return buffer().ReadUint16(error);
   }
 
-  /// Read int32_t value as zigzag varint from buffer.
-  inline Result<int32_t, Error> read_varint32() {
-    return buffer().ReadVarInt32();
+  /// Read int16_t value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE int16_t read_int16(Error *error) {
+    return buffer().ReadInt16(error);
   }
 
-  /// Read uint64_t value as varint from buffer.
-  inline Result<uint64_t, Error> read_varuint64() {
-    return buffer().ReadVarUint64();
+  /// Read uint32_t value from buffer (fixed 4 bytes). Sets error on failure.
+  FORY_ALWAYS_INLINE uint32_t read_uint32(Error *error) {
+    return buffer().ReadUint32(error);
   }
 
-  /// Read int64_t value as zigzag varint from buffer.
-  inline Result<int64_t, Error> read_varint64() {
-    return buffer().ReadVarInt64();
+  /// Read int32_t value from buffer (fixed 4 bytes). Sets error on failure.
+  FORY_ALWAYS_INLINE int32_t read_int32(Error *error) {
+    return buffer().ReadInt32(error);
   }
 
-  /// Read uint64_t value as varuint36small from buffer.
-  /// This is the special variable-length encoding used for string headers.
-  inline Result<uint64_t, Error> read_varuint36small() {
-    return buffer().ReadVarUint36Small();
+  /// Read uint64_t value from buffer (fixed 8 bytes). Sets error on failure.
+  FORY_ALWAYS_INLINE uint64_t read_uint64(Error *error) {
+    return buffer().ReadUint64(error);
+  }
+
+  /// Read int64_t value from buffer (fixed 8 bytes). Sets error on failure.
+  FORY_ALWAYS_INLINE int64_t read_int64(Error *error) {
+    return buffer().ReadInt64(error);
+  }
+
+  /// Read float value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE float read_float(Error *error) {
+    return buffer().ReadFloat(error);
+  }
+
+  /// Read double value from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE double read_double(Error *error) {
+    return buffer().ReadDouble(error);
+  }
+
+  /// Read uint32_t value as varint from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE uint32_t read_varuint32(Error *error) {
+    return buffer().ReadVarUint32(error);
+  }
+
+  /// Read int32_t value as zigzag varint from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE int32_t read_varint32(Error *error) {
+    return buffer().ReadVarInt32(error);
+  }
+
+  /// Read uint64_t value as varint from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE uint64_t read_varuint64(Error *error) {
+    return buffer().ReadVarUint64(error);
+  }
+
+  /// Read int64_t value as zigzag varint from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE int64_t read_varint64(Error *error) {
+    return buffer().ReadVarInt64(error);
+  }
+
+  /// Read uint64_t value as varuint36small from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE uint64_t read_varuint36small(Error *error) {
+    return buffer().ReadVarUint36Small(error);
+  }
+
+  /// Read raw bytes from buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE void read_bytes(void *data, uint32_t length,
+                                     Error *error) {
+    buffer().ReadBytes(data, length, error);
   }
 
-  /// Read raw bytes from buffer.
-  inline Result<void, Error> read_bytes(void *data, uint32_t length) {
-    return buffer().ReadBytes(data, length);
+  /// Skip bytes in buffer. Sets error on failure.
+  FORY_ALWAYS_INLINE void skip(uint32_t length, Error *error) {
+    buffer().Skip(length, error);
   }
 
   Result<const TypeInfo *, Error>
diff --git a/cpp/fory/serialization/enum_serializer.h 
b/cpp/fory/serialization/enum_serializer.h
index def97e1c3..e07acada5 100644
--- a/cpp/fory/serialization/enum_serializer.h
+++ b/cpp/fory/serialization/enum_serializer.h
@@ -99,7 +99,11 @@ struct Serializer<E, std::enable_if_t<std::is_enum_v<E>>> {
     // mirror that layout: consume a single null/not-null flag and
     // then read the ordinal.
     if (ctx.is_xlang() && !read_ref) {
-      FORY_TRY(flag, ctx.read_int8());
+      Error error;
+      int8_t flag = ctx.read_int8(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (flag == NULL_FLAG) {
         // Represent Java null as the default enum value.
         return E{};
@@ -123,7 +127,11 @@ struct Serializer<E, std::enable_if_t<std::is_enum_v<E>>> {
   }
 
   static inline Result<E, Error> read_data(ReadContext &ctx) {
-    FORY_TRY(raw_ordinal, ctx.read_varuint32());
+    Error error;
+    uint32_t raw_ordinal = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     OrdinalType ordinal = static_cast<OrdinalType>(raw_ordinal);
     E value{};
     if (!Metadata::from_ordinal(ordinal, &value)) {
diff --git a/cpp/fory/serialization/fory.h b/cpp/fory/serialization/fory.h
index 34f69ef38..5d3cb8e24 100644
--- a/cpp/fory/serialization/fory.h
+++ b/cpp/fory/serialization/fory.h
@@ -579,9 +579,11 @@ private:
     // Load TypeMetas at the beginning in compatible mode
     size_t bytes_to_skip = 0;
     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();
+      Error error;
+      int32_t meta_offset = buffer.ReadInt32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (meta_offset != -1) {
         FORY_TRY(meta_size, read_ctx_->load_type_meta(meta_offset));
         bytes_to_skip = meta_size;
diff --git a/cpp/fory/serialization/map_serializer.h 
b/cpp/fory/serialization/map_serializer.h
index 32abb9c8e..d30f20cc0 100644
--- a/cpp/fory/serialization/map_serializer.h
+++ b/cpp/fory/serialization/map_serializer.h
@@ -491,10 +491,14 @@ inline Result<MapType, Error> 
read_map_data_fast(ReadContext &ctx,
     return result;
   }
 
+  Error error;
   uint32_t len_counter = 0;
 
   while (len_counter < length) {
-    FORY_TRY(header, ctx.read_uint8());
+    uint8_t header = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Handle null entries (shouldn't happen in fast path, but be defensive)
     if ((header & KEY_NULL) && (header & VALUE_NULL)) {
@@ -516,7 +520,10 @@ inline Result<MapType, Error> 
read_map_data_fast(ReadContext &ctx,
     }
 
     // Read chunk size
-    FORY_TRY(chunk_size, ctx.read_uint8());
+    uint8_t chunk_size = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Read type info if not declared
     if (!(header & DECL_KEY_TYPE)) {
@@ -565,10 +572,14 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
   constexpr bool key_is_shared_ref = is_shared_ref_v<K>;
   constexpr bool val_is_shared_ref = is_shared_ref_v<V>;
 
+  Error error;
   uint32_t len_counter = 0;
 
   while (len_counter < length) {
-    FORY_TRY(header, ctx.read_uint8());
+    uint8_t header = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Handle null entries
     if ((header & KEY_NULL) && (header & VALUE_NULL)) {
@@ -676,7 +687,10 @@ inline Result<MapType, Error> 
read_map_data_slow(ReadContext &ctx,
     }
 
     // Non-null key and value chunk
-    FORY_TRY(chunk_size, ctx.read_uint8());
+    uint8_t chunk_size = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     bool key_declared = (header & DECL_KEY_TYPE) != 0;
     bool value_declared = (header & DECL_VALUE_TYPE) != 0;
     bool track_key_ref = (header & TRACKING_KEY_REF) != 0;
@@ -767,7 +781,7 @@ struct Serializer<std::map<K, V, Args...>> {
 
   static inline Result<void, Error> write_type_info(WriteContext &ctx) {
     ctx.write_varuint32(static_cast<uint32_t>(type_id));
-    return Result<void, Error>();
+    return {};
   }
 
   static inline Result<void, Error> read_type_info(ReadContext &ctx) {
@@ -776,7 +790,7 @@ struct Serializer<std::map<K, V, Args...>> {
       return Unexpected(Error::type_mismatch(type_info->type_id,
                                              static_cast<uint32_t>(type_id)));
     }
-    return Result<void, Error>();
+    return {};
   }
 
   // Match Rust signature: fory_write(&self, context, write_ref_info,
@@ -830,15 +844,22 @@ struct Serializer<std::map<K, V, Args...>> {
       return std::map<K, V, Args...>();
     }
 
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
 
-    FORY_TRY(length, ctx.read_varuint32());
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     constexpr bool is_fast_path =
         !is_polymorphic_v<K> && !is_polymorphic_v<V> && !is_shared_ref_v<K> &&
@@ -861,7 +882,11 @@ struct Serializer<std::map<K, V, Args...>> {
 
   static inline Result<std::map<K, V, Args...>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(length, ctx.read_varuint32());
+    Error error;
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     constexpr bool is_fast_path =
         !is_polymorphic_v<K> && !is_polymorphic_v<V> && !is_shared_ref_v<K> &&
@@ -941,15 +966,22 @@ struct Serializer<std::unordered_map<K, V, Args...>> {
       return std::unordered_map<K, V, Args...>();
     }
 
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
       }
     }
 
-    FORY_TRY(length, ctx.read_varuint32());
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     constexpr bool is_fast_path =
         !is_polymorphic_v<K> && !is_polymorphic_v<V> && !is_shared_ref_v<K> &&
@@ -974,7 +1006,11 @@ struct Serializer<std::unordered_map<K, V, Args...>> {
 
   static inline Result<std::unordered_map<K, V, Args...>, Error>
   read_data(ReadContext &ctx) {
-    FORY_TRY(length, ctx.read_varuint32());
+    Error error;
+    uint32_t length = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     constexpr bool is_fast_path =
         !is_polymorphic_v<K> && !is_polymorphic_v<V> && !is_shared_ref_v<K> &&
diff --git a/cpp/fory/serialization/serializer.h 
b/cpp/fory/serialization/serializer.h
index 7b0d6494a..9946db4fd 100644
--- a/cpp/fory/serialization/serializer.h
+++ b/cpp/fory/serialization/serializer.h
@@ -33,6 +33,37 @@
 namespace fory {
 namespace serialization {
 
+// ============================================================================
+// Error Handling Macros for Serialization
+// ============================================================================
+
+/// Return early if the error pointer indicates an error.
+/// Use this macro when reading struct fields with the Error* pattern.
+/// The macro checks the error state and returns an Unexpected with the error.
+///
+/// Example usage:
+/// ```cpp
+/// Error error;
+/// int32_t value = buffer.ReadVarInt32(&error);
+/// FORY_RETURN_IF_SERDE_ERROR(&error);
+/// // Use value...
+/// ```
+#define FORY_RETURN_IF_SERDE_ERROR(error_ptr)                                  
\
+  do {                                                                         
\
+    if (FORY_PREDICT_FALSE(!(error_ptr)->ok())) {                              
\
+      return ::fory::Unexpected(std::move(*(error_ptr)));                      
\
+    }                                                                          
\
+  } while (0)
+
+/// Return early if the error indicates an error, with a custom return type.
+/// Use this when the return type is not Result<T, Error>.
+#define FORY_RETURN_IF_SERDE_ERROR_WITH(error_ptr, return_type)                
\
+  do {                                                                         
\
+    if (FORY_PREDICT_FALSE(!(error_ptr)->ok())) {                              
\
+      return return_type(::fory::Unexpected(std::move(*(error_ptr))));         
\
+    }                                                                          
\
+  } while (0)
+
 // ============================================================================
 // Protocol Constants
 // ============================================================================
@@ -110,7 +141,11 @@ inline Result<HeaderInfo, Error> read_header(Buffer 
&buffer) {
 
   // Java writes a language byte after header in xlang mode - read and ignore 
it
   if (info.is_xlang) {
-    FORY_TRY(lang_byte, buffer.ReadUint8());
+    Error error;
+    uint8_t lang_byte = buffer.ReadUint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     info.language = static_cast<Language>(lang_byte);
   } else {
     info.language = Language::JAVA;
@@ -148,7 +183,11 @@ inline Result<bool, Error> consume_ref_flag(ReadContext 
&ctx, bool read_ref) {
   if (!read_ref) {
     return true;
   }
-  FORY_TRY(flag, ctx.read_int8());
+  Error error;
+  int8_t flag = ctx.read_int8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   if (flag == NULL_FLAG) {
     return false;
   }
@@ -156,7 +195,10 @@ inline Result<bool, Error> consume_ref_flag(ReadContext 
&ctx, bool read_ref) {
     return true;
   }
   if (flag == REF_FLAG) {
-    FORY_TRY(ref_id, ctx.read_varuint32());
+    uint32_t ref_id = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return Unexpected(Error::invalid_ref(
         "Unexpected reference flag for non-referencable value, ref id: " +
         std::to_string(ref_id)));
diff --git a/cpp/fory/serialization/skip.cc b/cpp/fory/serialization/skip.cc
index af073b476..33a92a3f7 100644
--- a/cpp/fory/serialization/skip.cc
+++ b/cpp/fory/serialization/skip.cc
@@ -53,13 +53,21 @@ inline bool field_need_write_ref_into_runtime(const 
FieldType &field_type) {
 
 Result<void, Error> skip_varint(ReadContext &ctx) {
   // Skip varint by reading it
-  FORY_RETURN_NOT_OK(ctx.read_varuint64());
+  Error error;
+  ctx.read_varuint64(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   return {};
 }
 
 Result<void, Error> skip_string(ReadContext &ctx) {
   // Read string length + encoding
-  FORY_TRY(size_encoding, ctx.read_varuint64());
+  Error error;
+  uint64_t size_encoding = ctx.read_varuint64(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   uint64_t size = size_encoding >> 2;
 
   // Skip string data
@@ -69,10 +77,17 @@ Result<void, Error> skip_string(ReadContext &ctx) {
 
 Result<void, Error> skip_list(ReadContext &ctx, const FieldType &field_type) {
   // Read list length
-  FORY_TRY(length, ctx.read_varuint64());
+  Error error;
+  uint64_t length = ctx.read_varuint64(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   // Read elements header
-  FORY_TRY(header, ctx.read_uint8());
+  uint8_t header = ctx.read_uint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   bool track_ref = (header & 0b1) != 0;
   bool has_null = (header & 0b10) != 0;
@@ -80,7 +95,10 @@ Result<void, Error> skip_list(ReadContext &ctx, const 
FieldType &field_type) {
 
   // If not declared type, skip element type info once
   if (!is_declared_type) {
-    FORY_RETURN_NOT_OK(ctx.read_uint8());
+    ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
   }
 
   // Get element type
@@ -97,13 +115,19 @@ Result<void, Error> skip_list(ReadContext &ctx, const 
FieldType &field_type) {
   for (uint64_t i = 0; i < length; ++i) {
     if (track_ref) {
       // Read and check ref flag
-      FORY_TRY(ref_flag, ctx.read_int8());
+      int8_t ref_flag = ctx.read_int8(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (ref_flag == NULL_FLAG || ref_flag == REF_FLAG) {
         continue; // Null or reference, already handled
       }
     } else if (has_null) {
       // Read null flag
-      FORY_TRY(null_flag, ctx.read_int8());
+      int8_t null_flag = ctx.read_int8(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (null_flag == NULL_FLAG) {
         continue; // Null value
       }
@@ -124,7 +148,11 @@ Result<void, Error> skip_set(ReadContext &ctx, const 
FieldType &field_type) {
 
 Result<void, Error> skip_map(ReadContext &ctx, const FieldType &field_type) {
   // Read map length
-  FORY_TRY(total_length, ctx.read_varuint64());
+  Error error;
+  uint64_t total_length = ctx.read_varuint64(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   // Get key and value types
   FieldType key_type, value_type;
@@ -140,10 +168,16 @@ Result<void, Error> skip_map(ReadContext &ctx, const 
FieldType &field_type) {
   uint64_t read_count = 0;
   while (read_count < total_length) {
     // Read chunk header
-    FORY_TRY(chunk_header, ctx.read_uint8());
+    uint8_t chunk_header = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Read chunk size
-    FORY_TRY(chunk_size, ctx.read_uint8());
+    uint8_t chunk_size = ctx.read_uint8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Extract flags from chunk header
     bool key_track_ref = (chunk_header & 0b1) != 0;
@@ -155,7 +189,10 @@ Result<void, Error> skip_map(ReadContext &ctx, const 
FieldType &field_type) {
     for (uint8_t i = 0; i < chunk_size; ++i) {
       // Skip key with ref flag if needed
       if (key_track_ref || key_has_null) {
-        FORY_TRY(key_ref, ctx.read_int8());
+        int8_t key_ref = ctx.read_int8(&error);
+        if (FORY_PREDICT_FALSE(!error.ok())) {
+          return Unexpected(std::move(error));
+        }
         if (key_ref != NOT_NULL_VALUE_FLAG && key_ref != REF_VALUE_FLAG) {
           continue; // Null or ref, skip value too
         }
@@ -164,7 +201,10 @@ Result<void, Error> skip_map(ReadContext &ctx, const 
FieldType &field_type) {
 
       // Skip value with ref flag if needed
       if (value_track_ref || value_has_null) {
-        FORY_TRY(val_ref, ctx.read_int8());
+        int8_t val_ref = ctx.read_int8(&error);
+        if (FORY_PREDICT_FALSE(!error.ok())) {
+          return Unexpected(std::move(error));
+        }
         if (val_ref != NOT_NULL_VALUE_FLAG && val_ref != REF_VALUE_FLAG) {
           continue; // Null or ref
         }
@@ -189,10 +229,17 @@ Result<void, Error> skip_struct(ReadContext &ctx, const 
FieldType &field_type) {
   }
 
   // Read remote type_id (ignored for now) and meta_index
-  FORY_TRY(remote_type_id, ctx.read_varuint32());
+  Error error;
+  uint32_t remote_type_id = ctx.read_varuint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   (void)remote_type_id;
 
-  FORY_TRY(meta_index, ctx.read_varuint32());
+  uint32_t meta_index = ctx.read_varuint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   FORY_TRY(type_info, ctx.get_type_info_by_index(meta_index));
   if (!type_info || !type_info->type_meta) {
     return Unexpected(
@@ -225,12 +272,19 @@ Result<void, Error> skip_ext(ReadContext &ctx, const 
FieldType &field_type) {
 
   const TypeInfo *type_info = nullptr;
 
+  Error error;
   if (tid == TypeId::NAMED_EXT) {
     // Named ext: read type_id and meta_index
-    FORY_TRY(remote_type_id, ctx.read_varuint32());
+    uint32_t remote_type_id = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     (void)remote_type_id;
 
-    FORY_TRY(meta_index, ctx.read_varuint32());
+    uint32_t meta_index = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     FORY_TRY(type_info_result, ctx.get_type_info_by_index(meta_index));
     type_info = type_info_result;
   } else {
@@ -274,9 +328,13 @@ Result<void, Error> skip_ext(ReadContext &ctx, const 
FieldType &field_type) {
 Result<void, Error> skip_field_value(ReadContext &ctx,
                                      const FieldType &field_type,
                                      bool read_ref_flag) {
+  Error error;
   // Read ref flag if needed
   if (read_ref_flag) {
-    FORY_TRY(ref_flag, ctx.read_int8());
+    int8_t ref_flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (ref_flag == NULL_FLAG || ref_flag == REF_FLAG) {
       return {}; // Null or reference, nothing more to skip
     }
@@ -306,8 +364,10 @@ Result<void, Error> skip_field_value(ReadContext &ctx,
     // INT32 values are encoded as varint32 in the C++
     // serializer, so we must consume a varint32 here
     // instead of assuming fixed-width encoding.
-    FORY_TRY(ignored_i32, ctx.read_varint32());
-    (void)ignored_i32;
+    ctx.read_varint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return {};
   }
 
@@ -319,8 +379,10 @@ Result<void, Error> skip_field_value(ReadContext &ctx,
     // INT64 values are encoded as varint64 in the C++
     // serializer, so we must consume a varint64 here
     // instead of assuming fixed-width encoding.
-    FORY_TRY(ignored_i64, ctx.read_varint64());
-    (void)ignored_i64;
+    ctx.read_varint64(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return {};
   }
 
@@ -384,7 +446,10 @@ Result<void, Error> skip_field_value(ReadContext &ctx,
   case TypeId::FLOAT32_ARRAY:
   case TypeId::FLOAT64_ARRAY: {
     // Read array length
-    FORY_TRY(len, ctx.read_varuint32());
+    uint32_t len = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     // Calculate element size
     size_t elem_size = 1;
@@ -411,7 +476,10 @@ Result<void, Error> skip_field_value(ReadContext &ctx,
 
   case TypeId::BINARY: {
     // Read binary length
-    FORY_TRY(len, ctx.read_varuint32());
+    uint32_t len = ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     ctx.buffer().IncreaseReaderIndex(len);
     return {};
   }
@@ -419,8 +487,10 @@ Result<void, Error> skip_field_value(ReadContext &ctx,
   case TypeId::ENUM:
   case TypeId::NAMED_ENUM: {
     // Enums are serialized as ordinal varuint32 values.
-    FORY_TRY(ignored, ctx.read_varuint32());
-    (void)ignored;
+    ctx.read_varuint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return {};
   }
 
diff --git a/cpp/fory/serialization/smart_ptr_serializers.h 
b/cpp/fory/serialization/smart_ptr_serializers.h
index f86e4e3da..c26d6bd84 100644
--- a/cpp/fory/serialization/smart_ptr_serializers.h
+++ b/cpp/fory/serialization/smart_ptr_serializers.h
@@ -102,7 +102,11 @@ template <typename T> struct Serializer<std::optional<T>> {
     }
 
     const uint32_t flag_pos = ctx.buffer().reader_index();
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     if (flag == NULL_FLAG) {
       return std::optional<T>(std::nullopt);
@@ -137,7 +141,11 @@ template <typename T> struct Serializer<std::optional<T>> {
     }
 
     const uint32_t flag_pos = ctx.buffer().reader_index();
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
 
     if (flag == NULL_FLAG) {
       return std::optional<T>(std::nullopt);
@@ -339,7 +347,11 @@ template <typename T> struct 
Serializer<std::shared_ptr<T>> {
     }
 
     // Handle read_ref=true case
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (flag == NULL_FLAG) {
       return std::shared_ptr<T>(nullptr);
     }
@@ -349,7 +361,10 @@ template <typename T> struct 
Serializer<std::shared_ptr<T>> {
         return Unexpected(Error::invalid_ref(
             "Reference flag encountered when reference tracking disabled"));
       }
-      FORY_TRY(ref_id, ctx.read_varuint32());
+      uint32_t ref_id = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return ctx.ref_reader().template get_shared_ref<T>(ref_id);
     }
 
@@ -432,7 +447,11 @@ template <typename T> struct 
Serializer<std::shared_ptr<T>> {
     }
 
     // Handle read_ref=true case
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (flag == NULL_FLAG) {
       return std::shared_ptr<T>(nullptr);
     }
@@ -442,7 +461,10 @@ template <typename T> struct 
Serializer<std::shared_ptr<T>> {
         return Unexpected(Error::invalid_ref(
             "Reference flag encountered when reference tracking disabled"));
       }
-      FORY_TRY(ref_id, ctx.read_varuint32());
+      uint32_t ref_id = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       return ctx.ref_reader().template get_shared_ref<T>(ref_id);
     }
 
@@ -639,7 +661,11 @@ template <typename T> struct 
Serializer<std::unique_ptr<T>> {
     }
 
     // Handle read_ref=true case
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (flag == NULL_FLAG) {
       return std::unique_ptr<T>(nullptr);
     }
@@ -705,7 +731,11 @@ template <typename T> struct 
Serializer<std::unique_ptr<T>> {
     }
 
     // Handle read_ref=true case
-    FORY_TRY(flag, ctx.read_int8());
+    Error error;
+    int8_t flag = ctx.read_int8(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     if (flag == NULL_FLAG) {
       return std::unique_ptr<T>(nullptr);
     }
diff --git a/cpp/fory/serialization/struct_serializer.h 
b/cpp/fory/serialization/struct_serializer.h
index e9b33ee3c..e62b3ba1a 100644
--- a/cpp/fory/serialization/struct_serializer.h
+++ b/cpp/fory/serialization/struct_serializer.h
@@ -874,6 +874,73 @@ Result<void, Error> write_struct_fields_impl(const T &obj, 
WriteContext &ctx,
   }
 }
 
+/// Type trait to check if a type is a raw primitive (not a wrapper like
+/// optional, shared_ptr, etc.)
+template <typename T> struct is_raw_primitive : std::false_type {};
+template <> struct is_raw_primitive<bool> : std::true_type {};
+template <> struct is_raw_primitive<int8_t> : std::true_type {};
+template <> struct is_raw_primitive<uint8_t> : std::true_type {};
+template <> struct is_raw_primitive<int16_t> : std::true_type {};
+template <> struct is_raw_primitive<uint16_t> : std::true_type {};
+template <> struct is_raw_primitive<int32_t> : std::true_type {};
+template <> struct is_raw_primitive<uint32_t> : std::true_type {};
+template <> struct is_raw_primitive<int64_t> : std::true_type {};
+template <> struct is_raw_primitive<uint64_t> : std::true_type {};
+template <> struct is_raw_primitive<float> : std::true_type {};
+template <> struct is_raw_primitive<double> : std::true_type {};
+template <typename T>
+inline constexpr bool is_raw_primitive_v = is_raw_primitive<T>::value;
+
+/// Helper to read a primitive field directly using Error* pattern.
+/// This bypasses Serializer<FieldType>::read for better performance.
+/// Returns the read value; sets error on failure.
+/// NOTE: Only use for raw primitive types, not wrappers!
+template <typename FieldType>
+FORY_ALWAYS_INLINE FieldType read_primitive_field_direct(ReadContext &ctx,
+                                                         Error *error) {
+  static_assert(is_raw_primitive_v<FieldType>,
+                "read_primitive_field_direct only supports raw primitives");
+
+  // Use the actual C++ type, not TypeId, because signed/unsigned types
+  // have different encoding (signed use varint, unsigned use fixed bytes).
+  if constexpr (std::is_same_v<FieldType, bool>) {
+    uint8_t v = ctx.read_uint8(error);
+    return v != 0;
+  } else if constexpr (std::is_same_v<FieldType, int8_t>) {
+    return ctx.read_int8(error);
+  } else if constexpr (std::is_same_v<FieldType, uint8_t>) {
+    return ctx.read_uint8(error);
+  } else if constexpr (std::is_same_v<FieldType, int16_t>) {
+    // int16_t uses fixed 2-byte encoding
+    return ctx.read_int16(error);
+  } else if constexpr (std::is_same_v<FieldType, uint16_t>) {
+    // uint16_t uses fixed 2-byte encoding
+    int16_t v = ctx.read_int16(error);
+    return static_cast<uint16_t>(v);
+  } else if constexpr (std::is_same_v<FieldType, int32_t>) {
+    // int32_t uses varint encoding
+    return ctx.read_varint32(error);
+  } else if constexpr (std::is_same_v<FieldType, uint32_t>) {
+    // uint32_t uses fixed 4-byte encoding (not varint!)
+    return static_cast<uint32_t>(ctx.read_int32(error));
+  } else if constexpr (std::is_same_v<FieldType, int64_t>) {
+    // int64_t uses varint encoding
+    return ctx.read_varint64(error);
+  } else if constexpr (std::is_same_v<FieldType, uint64_t>) {
+    // uint64_t uses fixed 8-byte encoding (not varint!)
+    return static_cast<uint64_t>(ctx.read_int64(error));
+  } else if constexpr (std::is_same_v<FieldType, float>) {
+    return ctx.read_float(error);
+  } else if constexpr (std::is_same_v<FieldType, double>) {
+    return ctx.read_double(error);
+  } else {
+    // Fallback for other types - should not be reached for primitives
+    static_assert(sizeof(FieldType) == 0,
+                  "Unexpected type in read_primitive_field_direct");
+    return FieldType{};
+  }
+}
+
 /// Helper to read a single field by index
 template <size_t Index, typename T>
 Result<void, Error> read_single_field_by_index(T &obj, ReadContext &ctx) {
@@ -923,9 +990,20 @@ Result<void, Error> read_single_field_by_index(T &obj, 
ReadContext &ctx) {
             << ", reader_index=" << ctx.buffer().reader_index() << std::endl;
 #endif
 
-  FORY_ASSIGN_OR_RETURN(obj.*field_ptr,
-                        Serializer<FieldType>::read(ctx, read_ref, read_type));
-  return Result<void, Error>();
+  // OPTIMIZATION: For raw primitive fields (not wrappers like optional,
+  // shared_ptr) that don't need ref metadata, bypass Serializer<T>::read
+  // and use direct buffer reads with Error*.
+  constexpr bool is_raw_prim = is_raw_primitive_v<FieldType>;
+  if constexpr (is_raw_prim && is_primitive_field && !field_needs_ref) {
+    Error error;
+    obj.*field_ptr = read_primitive_field_direct<FieldType>(ctx, &error);
+    FORY_RETURN_IF_SERDE_ERROR(&error);
+    return Result<void, Error>();
+  } else {
+    FORY_ASSIGN_OR_RETURN(
+        obj.*field_ptr, Serializer<FieldType>::read(ctx, read_ref, read_type));
+    return Result<void, Error>();
+  }
 }
 
 /// Helper to read a single field by index in compatible mode using
@@ -943,6 +1021,8 @@ read_single_field_by_index_compatible(T &obj, ReadContext 
&ctx,
   constexpr bool is_struct_field = is_fory_serializable_v<FieldType>;
   constexpr bool is_polymorphic_field =
       Serializer<FieldType>::type_id == TypeId::UNKNOWN;
+  constexpr TypeId field_type_id = Serializer<FieldType>::type_id;
+  constexpr bool is_primitive_field = is_primitive_type_id(field_type_id);
 
   bool read_type = is_polymorphic_field;
 
@@ -972,6 +1052,18 @@ read_single_field_by_index_compatible(T &obj, ReadContext 
&ctx,
             << ", reader_index=" << ctx.buffer().reader_index() << std::endl;
 #endif
 
+  // OPTIMIZATION: For raw primitive fields (not wrappers) with no ref flag,
+  // bypass Serializer<T>::read and use direct buffer reads with Error*.
+  constexpr bool is_raw_prim = is_raw_primitive_v<FieldType>;
+  if constexpr (is_raw_prim && is_primitive_field) {
+    if (!read_ref) {
+      Error error;
+      obj.*field_ptr = read_primitive_field_direct<FieldType>(ctx, &error);
+      FORY_RETURN_IF_SERDE_ERROR(&error);
+      return Result<void, Error>();
+    }
+  }
+
   FORY_ASSIGN_OR_RETURN(obj.*field_ptr,
                         Serializer<FieldType>::read(ctx, read_ref, read_type));
   return Result<void, Error>();
@@ -1271,9 +1363,12 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
                                bool read_type) {
     // Handle reference metadata
     int8_t ref_flag;
+    Error error;
     if (read_ref) {
-      FORY_TRY(flag, ctx.read_int8());
-      ref_flag = flag;
+      ref_flag = ctx.read_int8(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
 #ifdef FORY_DEBUG
       std::cerr << "[xlang][struct] T=" << typeid(T).name()
                 << ", read_ref_flag=" << static_cast<int>(ref_flag)
@@ -1295,7 +1390,10 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
         // In compatible mode: always use remote TypeMeta for schema evolution
         if (read_type) {
           // Read type_id
-          FORY_TRY(remote_type_id, ctx.read_varuint32());
+          uint32_t remote_type_id = ctx.read_varuint32(&error);
+          if (FORY_PREDICT_FALSE(!error.ok())) {
+            return Unexpected(std::move(error));
+          }
 
           // Check LOCAL type to decide if we should read meta_index (matches
           // Rust logic)
@@ -1310,7 +1408,10 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
                   static_cast<uint8_t>(TypeId::NAMED_COMPATIBLE_STRUCT)) {
             // Use meta sharing: read varint index and get TypeInfo from
             // meta_reader
-            FORY_TRY(meta_index, ctx.read_varuint32());
+            uint32_t meta_index = ctx.read_varuint32(&error);
+            if (FORY_PREDICT_FALSE(!error.ok())) {
+              return Unexpected(std::move(error));
+            }
             FORY_TRY(remote_type_info, ctx.get_type_info_by_index(meta_index));
 
             return read_compatible(ctx, remote_type_info);
@@ -1353,7 +1454,10 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
               expected_type_id_low !=
                   static_cast<uint8_t>(TypeId::NAMED_STRUCT)) {
             // Simple type ID - just read and compare varint directly
-            FORY_TRY(remote_type_id, ctx.read_varuint32());
+            uint32_t remote_type_id = ctx.read_varuint32(&error);
+            if (FORY_PREDICT_FALSE(!error.ok())) {
+              return Unexpected(std::move(error));
+            }
             if (remote_type_id != expected_type_id) {
               return Unexpected(
                   Error::type_mismatch(remote_type_id, expected_type_id));
@@ -1388,7 +1492,11 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
                                           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());
+      Error error;
+      int32_t read_version = ctx.buffer().ReadInt32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       FORY_TRY(local_type_info,
                ctx.type_resolver().template get_type_info<T>());
       if (!local_type_info->type_meta) {
@@ -1421,7 +1529,11 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
   static Result<T, Error> read_data(ReadContext &ctx) {
     if (ctx.check_struct_version()) {
-      FORY_TRY(read_version, ctx.buffer().ReadInt32());
+      Error error;
+      int32_t read_version = ctx.buffer().ReadInt32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       FORY_TRY(local_type_info,
                ctx.type_resolver().template get_type_info<T>());
       if (!local_type_info->type_meta) {
diff --git a/cpp/fory/serialization/temporal_serializers.h 
b/cpp/fory/serialization/temporal_serializers.h
index 0a315fc76..c4eb1e0f2 100644
--- a/cpp/fory/serialization/temporal_serializers.h
+++ b/cpp/fory/serialization/temporal_serializers.h
@@ -87,8 +87,12 @@ template <> struct Serializer<Duration> {
     if (!has_value) {
       return Duration(0);
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -98,8 +102,12 @@ template <> struct Serializer<Duration> {
   }
 
   static Result<Duration, Error> read_data(ReadContext &ctx) {
+    Error error;
     int64_t nanos;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&nanos, sizeof(int64_t)));
+    ctx.read_bytes(&nanos, sizeof(int64_t), &error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return Duration(nanos);
   }
 };
@@ -142,8 +150,12 @@ template <> struct Serializer<Timestamp> {
     if (!has_value) {
       return Timestamp(Duration(0));
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -153,8 +165,12 @@ template <> struct Serializer<Timestamp> {
   }
 
   static Result<Timestamp, Error> read_data(ReadContext &ctx) {
+    Error error;
     int64_t nanos;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&nanos, sizeof(int64_t)));
+    ctx.read_bytes(&nanos, sizeof(int64_t), &error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return Timestamp(Duration(nanos));
   }
 };
@@ -195,8 +211,12 @@ template <> struct Serializer<LocalDate> {
     if (!has_value) {
       return LocalDate();
     }
+    Error error;
     if (read_type) {
-      FORY_TRY(type_id_read, ctx.read_varuint32());
+      uint32_t type_id_read = ctx.read_varuint32(&error);
+      if (FORY_PREDICT_FALSE(!error.ok())) {
+        return Unexpected(std::move(error));
+      }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         return Unexpected(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
@@ -206,8 +226,12 @@ template <> struct Serializer<LocalDate> {
   }
 
   static Result<LocalDate, Error> read_data(ReadContext &ctx) {
+    Error error;
     LocalDate date;
-    FORY_RETURN_NOT_OK(ctx.read_bytes(&date.days_since_epoch, 
sizeof(int32_t)));
+    ctx.read_bytes(&date.days_since_epoch, sizeof(int32_t), &error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     return date;
   }
 };
diff --git a/cpp/fory/serialization/type_resolver.cc 
b/cpp/fory/serialization/type_resolver.cc
index 98f5c1341..cf37d3e71 100644
--- a/cpp/fory/serialization/type_resolver.cc
+++ b/cpp/fory/serialization/type_resolver.cc
@@ -80,7 +80,11 @@ Result<void, Error> FieldType::write_to(Buffer &buffer, bool 
write_flag,
 
 Result<FieldType, Error> FieldType::read_from(Buffer &buffer, bool read_flag,
                                               bool nullable_val) {
-  FORY_TRY(header, buffer.ReadVarUint32());
+  Error error;
+  uint32_t header = buffer.ReadVarUint32(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   uint32_t tid;
   bool null;
@@ -149,7 +153,11 @@ Result<std::vector<uint8_t>, Error> FieldInfo::to_bytes() 
const {
 
 Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer &buffer) {
   // Read field header
-  FORY_TRY(header, buffer.ReadUint8());
+  Error error;
+  uint8_t header = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   // Decode header layout:
   // bits 0: ref tracking flag
@@ -160,7 +168,10 @@ Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer 
&buffer) {
   bool nullable = (header & 0b10u) != 0;
   size_t name_size = ((header >> 2) & FIELD_NAME_SIZE_THRESHOLD);
   if (name_size == FIELD_NAME_SIZE_THRESHOLD) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     name_size += extra;
   }
   name_size += 1;
@@ -176,8 +187,10 @@ Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer 
&buffer) {
   // special characters (same as Encoders.FIELD_NAME_DECODER).
 
   std::vector<uint8_t> name_bytes(name_size);
-  FORY_RETURN_NOT_OK(
-      buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size)));
+  buffer.ReadBytes(name_bytes.data(), static_cast<uint32_t>(name_size), 
&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   static const MetaStringDecoder kFieldNameDecoder('$', '_');
 
@@ -230,7 +243,11 @@ inline Result<void, Error> write_meta_name(Buffer &buffer,
 inline Result<std::string, Error>
 read_meta_name(Buffer &buffer, const MetaStringDecoder &decoder,
                const MetaEncoding *encodings, size_t enc_count) {
-  FORY_TRY(header, buffer.ReadUint8());
+  Error error;
+  uint8_t header = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
   uint8_t encoding_idx = header & 0x3u;
   uint8_t length_prefix = header >> 2;
 
@@ -242,14 +259,19 @@ read_meta_name(Buffer &buffer, const MetaStringDecoder 
&decoder,
 
   size_t length = length_prefix;
   if (length >= BIG_NAME_THRESHOLD) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     length = BIG_NAME_THRESHOLD + static_cast<size_t>(extra);
   }
 
   std::vector<uint8_t> bytes(length);
   if (length > 0) {
-    FORY_RETURN_NOT_OK(
-        buffer.ReadBytes(bytes.data(), static_cast<uint32_t>(length)));
+    buffer.ReadBytes(bytes.data(), static_cast<uint32_t>(length), &error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
   }
 
   MetaEncoding encoding = encodings[encoding_idx];
@@ -341,26 +363,39 @@ TypeMeta::from_bytes(Buffer &buffer, const TypeMeta 
*local_type_info) {
   size_t start_pos = buffer.reader_index();
 
   // Read global binary header
+  Error error;
   int64_t header;
-  FORY_RETURN_NOT_OK(buffer.ReadBytes(&header, sizeof(header)));
+  buffer.ReadBytes(&header, sizeof(header), &error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   size_t header_size = sizeof(header);
   int64_t meta_size = header & META_SIZE_MASK;
   if (meta_size == META_SIZE_MASK) {
     uint32_t before = buffer.reader_index();
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     meta_size += extra;
     uint32_t after = buffer.reader_index();
     header_size += (after - before);
   }
   int64_t meta_hash = header >> (64 - NUM_HASH_BITS);
   // Read meta header
-  FORY_TRY(meta_header, buffer.ReadUint8());
+  uint8_t meta_header = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   bool register_by_name = (meta_header & REGISTER_BY_NAME_FLAG) != 0;
   size_t num_fields = meta_header & SMALL_NUM_FIELDS_THRESHOLD;
   if (num_fields == SMALL_NUM_FIELDS_THRESHOLD) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     num_fields += extra;
   }
 
@@ -383,7 +418,10 @@ TypeMeta::from_bytes(Buffer &buffer, const TypeMeta 
*local_type_info) {
                                     sizeof(kTypeNameEncodings[0])));
     type_name = std::move(tn);
   } else {
-    FORY_TRY(tid, buffer.ReadVarUint32());
+    uint32_t tid = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     type_id = tid;
   }
 
@@ -424,11 +462,15 @@ TypeMeta::from_bytes(Buffer &buffer, const TypeMeta 
*local_type_info) {
 
 Result<std::unique_ptr<TypeMeta>, Error>
 TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t header) {
+  Error error;
   int64_t meta_size = header & META_SIZE_MASK;
   size_t header_size = 0;
   if (meta_size == META_SIZE_MASK) {
     uint32_t before = buffer.reader_index();
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     meta_size += extra;
     uint32_t after = buffer.reader_index();
     header_size = (after - before);
@@ -438,12 +480,18 @@ TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t 
header) {
   size_t start_pos = buffer.reader_index();
 
   // Read meta header
-  FORY_TRY(meta_header, buffer.ReadUint8());
+  uint8_t meta_header = buffer.ReadUint8(&error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
 
   bool register_by_name = (meta_header & REGISTER_BY_NAME_FLAG) != 0;
   size_t num_fields = meta_header & SMALL_NUM_FIELDS_THRESHOLD;
   if (num_fields == SMALL_NUM_FIELDS_THRESHOLD) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     num_fields += extra;
   }
 
@@ -466,7 +514,10 @@ TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t 
header) {
                                     sizeof(kTypeNameEncodings[0])));
     type_name = std::move(tn);
   } else {
-    FORY_TRY(tid, buffer.ReadVarUint32());
+    uint32_t tid = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     type_id = tid;
   }
 
@@ -501,12 +552,20 @@ TypeMeta::from_bytes_with_header(Buffer &buffer, int64_t 
header) {
 }
 
 Result<void, Error> TypeMeta::skip_bytes(Buffer &buffer, int64_t header) {
+  Error error;
   int64_t meta_size = header & META_SIZE_MASK;
   if (meta_size == META_SIZE_MASK) {
-    FORY_TRY(extra, buffer.ReadVarUint32());
+    uint32_t extra = buffer.ReadVarUint32(&error);
+    if (FORY_PREDICT_FALSE(!error.ok())) {
+      return Unexpected(std::move(error));
+    }
     meta_size += extra;
   }
-  return buffer.Skip(meta_size);
+  buffer.Skip(static_cast<uint32_t>(meta_size), &error);
+  if (FORY_PREDICT_FALSE(!error.ok())) {
+    return Unexpected(std::move(error));
+  }
+  return Result<void, Error>();
 }
 
 Result<void, Error>
diff --git a/cpp/fory/serialization/xlang_test_main.cc 
b/cpp/fory/serialization/xlang_test_main.cc
index 587bb7434..8039b81f2 100644
--- a/cpp/fory/serialization/xlang_test_main.cc
+++ b/cpp/fory/serialization/xlang_test_main.cc
@@ -456,50 +456,43 @@ void RunTestBuffer(const std::string &data_file) {
   auto bytes = ReadFile(data_file);
   Buffer buffer(bytes.data(), static_cast<uint32_t>(bytes.size()), false);
 
-  auto bool_val_result = buffer.ReadUint8();
-  if (!bool_val_result.ok())
-    Fail("Failed to read bool: " + bool_val_result.error().message());
-  bool bool_val = bool_val_result.value() != 0;
-
-  auto int8_val_result = buffer.ReadInt8();
-  if (!int8_val_result.ok())
-    Fail("Failed to read int8: " + int8_val_result.error().message());
-  int8_t int8_val = int8_val_result.value();
-
-  auto int16_val_result = buffer.ReadInt16();
-  if (!int16_val_result.ok())
-    Fail("Failed to read int16: " + int16_val_result.error().message());
-  int16_t int16_val = int16_val_result.value();
-
-  auto int32_val_result = buffer.ReadInt32();
-  if (!int32_val_result.ok())
-    Fail("Failed to read int32: " + int32_val_result.error().message());
-  int32_t int32_val = int32_val_result.value();
-
-  auto int64_val_result = buffer.ReadInt64();
-  if (!int64_val_result.ok())
-    Fail("Failed to read int64: " + int64_val_result.error().message());
-  int64_t int64_val = int64_val_result.value();
-
-  auto float_val_result = buffer.ReadFloat();
-  if (!float_val_result.ok())
-    Fail("Failed to read float: " + float_val_result.error().message());
-  float float_val = float_val_result.value();
-
-  auto double_val_result = buffer.ReadDouble();
-  if (!double_val_result.ok())
-    Fail("Failed to read double: " + double_val_result.error().message());
-  double double_val = double_val_result.value();
-
-  auto varint_result = buffer.ReadVarUint32();
-  if (!varint_result.ok())
-    Fail("Failed to read varint: " + varint_result.error().message());
-  uint32_t varint = varint_result.value();
-
-  auto payload_len_result = buffer.ReadInt32();
-  if (!payload_len_result.ok())
-    Fail("Failed to read payload len: " + 
payload_len_result.error().message());
-  int32_t payload_len = payload_len_result.value();
+  Error error;
+  uint8_t bool_val_raw = buffer.ReadUint8(&error);
+  if (!error.ok())
+    Fail("Failed to read bool: " + error.message());
+  bool bool_val = bool_val_raw != 0;
+
+  int8_t int8_val = buffer.ReadInt8(&error);
+  if (!error.ok())
+    Fail("Failed to read int8: " + error.message());
+
+  int16_t int16_val = buffer.ReadInt16(&error);
+  if (!error.ok())
+    Fail("Failed to read int16: " + error.message());
+
+  int32_t int32_val = buffer.ReadInt32(&error);
+  if (!error.ok())
+    Fail("Failed to read int32: " + error.message());
+
+  int64_t int64_val = buffer.ReadInt64(&error);
+  if (!error.ok())
+    Fail("Failed to read int64: " + error.message());
+
+  float float_val = buffer.ReadFloat(&error);
+  if (!error.ok())
+    Fail("Failed to read float: " + error.message());
+
+  double double_val = buffer.ReadDouble(&error);
+  if (!error.ok())
+    Fail("Failed to read double: " + error.message());
+
+  uint32_t varint = buffer.ReadVarUint32(&error);
+  if (!error.ok())
+    Fail("Failed to read varint: " + error.message());
+
+  int32_t payload_len = buffer.ReadInt32(&error);
+  if (!error.ok())
+    Fail("Failed to read payload len: " + error.message());
 
   if (payload_len < 0 || buffer.reader_index() + payload_len > buffer.size()) {
     Fail("Invalid payload length in buffer test");
@@ -507,9 +500,9 @@ void RunTestBuffer(const std::string &data_file) {
   std::vector<uint8_t> payload(bytes.begin() + buffer.reader_index(),
                                bytes.begin() + buffer.reader_index() +
                                    payload_len);
-  auto skip_result = buffer.Skip(payload_len);
-  if (!skip_result.ok())
-    Fail("Failed to skip payload: " + skip_result.error().message());
+  buffer.Skip(payload_len, &error);
+  if (!error.ok())
+    Fail("Failed to skip payload: " + error.message());
 
   if (!bool_val || int8_val != std::numeric_limits<int8_t>::max() ||
       int16_val != std::numeric_limits<int16_t>::max() ||
@@ -541,6 +534,7 @@ void RunTestBufferVar(const std::string &data_file) {
   auto bytes = ReadFile(data_file);
   Buffer buffer(bytes.data(), static_cast<uint32_t>(bytes.size()), false);
 
+  Error error;
   const std::vector<int32_t> expected_varint32 = {
       std::numeric_limits<int32_t>::min(),
       std::numeric_limits<int32_t>::min() + 1,
@@ -561,8 +555,8 @@ void RunTestBufferVar(const std::string &data_file) {
       std::numeric_limits<int32_t>::max() - 1,
       std::numeric_limits<int32_t>::max()};
   for (int32_t value : expected_varint32) {
-    auto result = buffer.ReadVarInt32();
-    if (!result.ok() || result.value() != value) {
+    int32_t result = buffer.ReadVarInt32(&error);
+    if (!error.ok() || result != value) {
       Fail("VarInt32 mismatch");
     }
   }
@@ -571,8 +565,8 @@ void RunTestBufferVar(const std::string &data_file) {
       0u,       1u,       127u,       128u,       16383u,      16384u,
       2097151u, 2097152u, 268435455u, 268435456u, 2147483646u, 2147483647u};
   for (uint32_t value : expected_varuint32) {
-    auto result = buffer.ReadVarUint32();
-    if (!result.ok() || result.value() != value) {
+    uint32_t result = buffer.ReadVarUint32(&error);
+    if (!error.ok() || result != value) {
       Fail("VarUint32 mismatch");
     }
   }
@@ -598,8 +592,8 @@ void RunTestBufferVar(const std::string &data_file) {
       72057594037927936ull,
       static_cast<uint64_t>(std::numeric_limits<int64_t>::max())};
   for (uint64_t value : expected_varuint64) {
-    auto result = buffer.ReadVarUint64();
-    if (!result.ok() || result.value() != value) {
+    uint64_t result = buffer.ReadVarUint64(&error);
+    if (!error.ok() || result != value) {
       Fail("VarUint64 mismatch");
     }
   }
@@ -621,8 +615,8 @@ void RunTestBufferVar(const std::string &data_file) {
       std::numeric_limits<int64_t>::max() - 1,
       std::numeric_limits<int64_t>::max()};
   for (int64_t value : expected_varint64) {
-    auto result = buffer.ReadVarInt64();
-    if (!result.ok() || result.value() != value) {
+    int64_t result = buffer.ReadVarInt64(&error);
+    if (!error.ok() || result != value) {
       Fail("VarInt64 mismatch");
     }
   }
@@ -653,15 +647,14 @@ void RunTestMurmurHash3(const std::string &data_file) {
   }
   Buffer buffer(bytes.data(), static_cast<uint32_t>(bytes.size()), false);
 
-  auto first_result = buffer.ReadInt64();
-  if (!first_result.ok())
-    Fail("Failed to read first int64: " + first_result.error().message());
-  int64_t first = first_result.value();
+  Error error;
+  int64_t first = buffer.ReadInt64(&error);
+  if (!error.ok())
+    Fail("Failed to read first int64: " + error.message());
 
-  auto second_result = buffer.ReadInt64();
-  if (!second_result.ok())
-    Fail("Failed to read second int64: " + second_result.error().message());
-  int64_t second = second_result.value();
+  int64_t second = buffer.ReadInt64(&error);
+  if (!error.ok())
+    Fail("Failed to read second int64: " + error.message());
 
   int64_t hash_out[2] = {0, 0};
   MurmurHash3_x64_128("\x01\x02\x08", 3, 47, hash_out);
diff --git a/cpp/fory/util/buffer.h b/cpp/fory/util/buffer.h
index f64ebec45..8334ba596 100644
--- a/cpp/fory/util/buffer.h
+++ b/cpp/fory/util/buffer.h
@@ -583,90 +583,132 @@ public:
 
   // 
===========================================================================
   // Safe read methods with bounds checking
+  // All methods accept Error* as parameter for reduced overhead.
+  // On success, error->ok() remains true. On failure, error is set.
   // 
===========================================================================
 
-  /// Read uint8_t value from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<uint8_t, Error> ReadUint8() {
+  /// Read uint8_t value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE uint8_t ReadUint8(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     uint8_t value = data_[reader_index_];
     reader_index_ += 1;
     return value;
   }
 
-  /// Read int8_t value from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int8_t, Error> ReadInt8() {
+  /// Read int8_t value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE int8_t ReadInt8(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     int8_t value = static_cast<int8_t>(data_[reader_index_]);
     reader_index_ += 1;
     return value;
   }
 
-  /// Read int16_t value as fixed 2 bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int16_t, Error> ReadInt16() {
+  /// Read uint16_t value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE uint16_t ReadUint16(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 2 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 2, size_));
+      error->set_buffer_out_of_bound(reader_index_, 2, size_);
+      return 0;
+    }
+    uint16_t value =
+        reinterpret_cast<const uint16_t *>(data_ + reader_index_)[0];
+    reader_index_ += 2;
+    return value;
+  }
+
+  /// Read int16_t value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE int16_t ReadInt16(Error *error) {
+    if (FORY_PREDICT_FALSE(reader_index_ + 2 > size_)) {
+      error->set_buffer_out_of_bound(reader_index_, 2, size_);
+      return 0;
     }
     int16_t value = reinterpret_cast<const int16_t *>(data_ + 
reader_index_)[0];
     reader_index_ += 2;
     return value;
   }
 
-  /// Read int32_t value as fixed 4 bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int32_t, Error> ReadInt32() {
+  /// Read uint32_t value from buffer (fixed 4 bytes). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE uint32_t ReadUint32(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 4 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 4, size_));
+      error->set_buffer_out_of_bound(reader_index_, 4, size_);
+      return 0;
+    }
+    uint32_t value =
+        reinterpret_cast<const uint32_t *>(data_ + reader_index_)[0];
+    reader_index_ += 4;
+    return value;
+  }
+
+  /// Read int32_t value from buffer (fixed 4 bytes). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE int32_t ReadInt32(Error *error) {
+    if (FORY_PREDICT_FALSE(reader_index_ + 4 > size_)) {
+      error->set_buffer_out_of_bound(reader_index_, 4, size_);
+      return 0;
     }
     int32_t value = reinterpret_cast<const int32_t *>(data_ + 
reader_index_)[0];
     reader_index_ += 4;
     return value;
   }
 
-  /// Read int64_t value as fixed 8 bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int64_t, Error> ReadInt64() {
+  /// Read uint64_t value from buffer (fixed 8 bytes). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE uint64_t ReadUint64(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 8 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 8, size_));
+      error->set_buffer_out_of_bound(reader_index_, 8, size_);
+      return 0;
+    }
+    uint64_t value =
+        reinterpret_cast<const uint64_t *>(data_ + reader_index_)[0];
+    reader_index_ += 8;
+    return value;
+  }
+
+  /// Read int64_t value from buffer (fixed 8 bytes). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE int64_t ReadInt64(Error *error) {
+    if (FORY_PREDICT_FALSE(reader_index_ + 8 > size_)) {
+      error->set_buffer_out_of_bound(reader_index_, 8, size_);
+      return 0;
     }
     int64_t value = reinterpret_cast<const int64_t *>(data_ + 
reader_index_)[0];
     reader_index_ += 8;
     return value;
   }
 
-  /// Read float value as fixed 4 bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<float, Error> ReadFloat() {
+  /// Read float value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE float ReadFloat(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 4 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 4, size_));
+      error->set_buffer_out_of_bound(reader_index_, 4, size_);
+      return 0.0f;
     }
     float value = reinterpret_cast<const float *>(data_ + reader_index_)[0];
     reader_index_ += 4;
     return value;
   }
 
-  /// Read double value as fixed 8 bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<double, Error> ReadDouble() {
+  /// Read double value from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE double ReadDouble(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 8 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 8, size_));
+      error->set_buffer_out_of_bound(reader_index_, 8, size_);
+      return 0.0;
     }
     double value = reinterpret_cast<const double *>(data_ + reader_index_)[0];
     reader_index_ += 8;
     return value;
   }
 
-  /// Read uint32_t value as varint from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<uint32_t, Error> ReadVarUint32() {
+  /// Read uint32_t value as varint from buffer. Sets error on bounds 
violation.
+  FORY_ALWAYS_INLINE uint32_t ReadVarUint32(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     uint32_t read_bytes = 0;
     uint32_t value = GetVarUint32(reader_index_, &read_bytes);
@@ -674,24 +716,24 @@ public:
     return value;
   }
 
-  /// Read int32_t value as varint (zigzag encoded) from buffer at current
-  /// reader index. Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int32_t, Error> ReadVarInt32() {
+  /// Read int32_t value as varint (zigzag encoded). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE int32_t ReadVarInt32(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     uint32_t read_bytes = 0;
     uint32_t raw = GetVarUint32(reader_index_, &read_bytes);
     IncreaseReaderIndex(read_bytes);
-    int32_t value = static_cast<int32_t>((raw >> 1) ^ (~(raw & 1) + 1));
-    return value;
+    return static_cast<int32_t>((raw >> 1) ^ (~(raw & 1) + 1));
   }
 
-  /// Read uint64_t value as varint from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<uint64_t, Error> ReadVarUint64() {
+  /// Read uint64_t value as varint from buffer. Sets error on bounds 
violation.
+  FORY_ALWAYS_INLINE uint64_t ReadVarUint64(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     uint32_t read_bytes = 0;
     uint64_t value = GetVarUint64(reader_index_, &read_bytes);
@@ -699,24 +741,18 @@ public:
     return value;
   }
 
-  /// Read int64_t value as varint (zigzag encoded) from buffer at current
-  /// reader index. Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<int64_t, Error> ReadVarInt64() {
-    auto result = ReadVarUint64();
-    if (FORY_PREDICT_FALSE(!result.ok())) {
-      return Unexpected(result.error());
-    }
-    uint64_t raw = result.value();
-    int64_t value = static_cast<int64_t>((raw >> 1) ^ (~(raw & 1) + 1));
-    return value;
+  /// Read int64_t value as varint (zigzag encoded). Sets error on bounds
+  /// violation.
+  FORY_ALWAYS_INLINE int64_t ReadVarInt64(Error *error) {
+    uint64_t raw = ReadVarUint64(error);
+    return static_cast<int64_t>((raw >> 1) ^ (~(raw & 1) + 1));
   }
 
-  /// Read uint64_t value as varuint36small from buffer at current reader 
index.
-  /// This is the special variable-length encoding used for string headers.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<uint64_t, Error> ReadVarUint36Small() {
+  /// Read uint64_t value as varuint36small. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE uint64_t ReadVarUint36Small(Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + 1 > size_)) {
-      return Unexpected(Error::buffer_out_of_bound(reader_index_, 1, size_));
+      error->set_buffer_out_of_bound(reader_index_, 1, size_);
+      return 0;
     }
     uint32_t offset = reader_index_;
     // Fast path: need at least 8 bytes for safe bulk read
@@ -772,28 +808,23 @@ public:
     return result;
   }
 
-  /// Read raw bytes from buffer at current reader index.
-  /// Advances reader index and checks bounds.
-  FORY_ALWAYS_INLINE Result<void, Error> ReadBytes(void *data,
-                                                   uint32_t length) {
+  /// Read raw bytes from buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE void ReadBytes(void *data, uint32_t length, Error *error) 
{
     if (FORY_PREDICT_FALSE(reader_index_ + length > size_)) {
-      return Unexpected(
-          Error::buffer_out_of_bound(reader_index_, length, size_));
+      error->set_buffer_out_of_bound(reader_index_, length, size_);
+      return;
     }
     Copy(reader_index_, length, static_cast<uint8_t *>(data));
     IncreaseReaderIndex(length);
-    return Result<void, Error>();
   }
 
-  /// Skip bytes in buffer by advancing reader index.
-  /// Checks bounds to ensure we don't skip past the end.
-  FORY_ALWAYS_INLINE Result<void, Error> Skip(uint32_t length) {
+  /// Skip bytes in buffer. Sets error on bounds violation.
+  FORY_ALWAYS_INLINE void Skip(uint32_t length, Error *error) {
     if (FORY_PREDICT_FALSE(reader_index_ + length > size_)) {
-      return Unexpected(
-          Error::buffer_out_of_bound(reader_index_, length, size_));
+      error->set_buffer_out_of_bound(reader_index_, length, size_);
+      return;
     }
     IncreaseReaderIndex(length);
-    return Result<void, Error>();
   }
 
   /// Return true if both buffers are the same size and contain the same bytes
diff --git a/cpp/fory/util/error.h b/cpp/fory/util/error.h
index 3679c7b19..49a8a73db 100644
--- a/cpp/fory/util/error.h
+++ b/cpp/fory/util/error.h
@@ -51,29 +51,30 @@ enum class ErrorCode : char {
 
 /// Error class for Fory serialization and deserialization operations.
 ///
-/// # IMPORTANT: Always Use Static Constructor Functions
+/// This class supports two usage patterns:
+/// 1. Static factory functions for creating new errors (recommended for 
general
+/// use)
+/// 2. Mutable error pattern for performance-critical read paths
 ///
-/// **DO NOT** construct errors directly using the constructor.
-/// **ALWAYS** use the provided static factory functions instead.
-///
-/// ## Why Use Static Functions?
-///
-/// The static factory functions provide:
-/// - Consistent error creation across the codebase
-/// - Better ergonomics and readability
-/// - Future-proof API if error construction logic needs to change
-/// - Clear semantic meaning for each error type
-///
-/// ## Examples
+/// ## Pattern 1: Static Factory Functions (General Use)
 ///
 /// ```cpp
 /// // ✅ CORRECT: Use static factory functions
 /// auto err = Error::type_error("Expected string type");
 /// auto err = Error::invalid_data("Invalid value: " + std::to_string(42));
 /// auto err = Error::type_mismatch(1, 2);
+/// ```
 ///
-/// // ❌ WRONG: Do not construct directly
-/// // auto err = Error(ErrorCode::TypeError, "Expected string type");
+/// ## Pattern 2: Mutable Error for Read Paths (Performance Critical)
+///
+/// For high-performance read paths, use a stack-allocated Error and pass
+/// pointer:
+/// ```cpp
+/// Error error;  // Default: ok_ = true
+/// buffer.ReadInt32(&error);
+/// if (FORY_PREDICT_FALSE(!error.ok())) {
+///   return Unexpected(std::move(error));
+/// }
 /// ```
 ///
 /// ## Available Constructor Functions
@@ -97,6 +98,9 @@ enum class ErrorCode : char {
 /// - Error::unknown() - For generic errors
 class Error {
 public:
+  /// Default constructor - creates an "OK" (no error) state.
+  /// Used for stack-allocated error variables in read paths.
+  Error() : ok_(true), state_(nullptr) {}
   // Static factory functions - Use these instead of constructors!
 
   /// Creates a type mismatch error with the given type IDs.
@@ -198,8 +202,46 @@ public:
   }
 
   // Accessors
-  ErrorCode code() const { return state_->code_; }
-  const std::string &message() const { return state_->msg_; }
+
+  /// Returns true if this Error is in the "OK" (no error) state.
+  bool ok() const { return ok_; }
+
+  ErrorCode code() const {
+    if (ok_) {
+      return ErrorCode::OK;
+    }
+    return state_->code_;
+  }
+
+  const std::string &message() const {
+    static const std::string empty_msg;
+    if (ok_) {
+      return empty_msg;
+    }
+    return state_->msg_;
+  }
+
+  /// Set the error state. Used for mutable error pattern in read paths.
+  /// After calling this, ok() will return false.
+  void set_error(ErrorCode code, std::string msg) {
+    ok_ = false;
+    state_.reset(new ErrorState(code, std::move(msg)));
+  }
+
+  /// Set buffer out of bound error (common case optimization).
+  void set_buffer_out_of_bound(size_t offset, size_t length, size_t capacity) {
+    ok_ = false;
+    state_.reset(new ErrorState(
+        ErrorCode::BufferOutOfBound,
+        "Buffer out of bound: " + std::to_string(offset) + " + " +
+            std::to_string(length) + " > " + std::to_string(capacity)));
+  }
+
+  /// Reset to OK state.
+  void reset() {
+    ok_ = true;
+    state_.reset();
+  }
 
   /// Returns a string representation of this error.
   std::string to_string() const;
@@ -211,15 +253,34 @@ public:
   static ErrorCode string_to_code(const std::string &str);
 
   // Copy and move semantics
-  Error(const Error &other) : state_(new ErrorState(*other.state_)) {}
-  Error(Error &&) noexcept = default;
+  Error(const Error &other) : ok_(other.ok_) {
+    if (!ok_ && other.state_) {
+      state_.reset(new ErrorState(*other.state_));
+    }
+  }
+  Error(Error &&other) noexcept
+      : ok_(other.ok_), state_(std::move(other.state_)) {
+    other.ok_ = true;
+  }
   Error &operator=(const Error &other) {
     if (this != &other) {
-      state_.reset(new ErrorState(*other.state_));
+      ok_ = other.ok_;
+      if (!ok_ && other.state_) {
+        state_.reset(new ErrorState(*other.state_));
+      } else {
+        state_.reset();
+      }
+    }
+    return *this;
+  }
+  Error &operator=(Error &&other) noexcept {
+    if (this != &other) {
+      ok_ = other.ok_;
+      state_ = std::move(other.state_);
+      other.ok_ = true;
     }
     return *this;
   }
-  Error &operator=(Error &&) noexcept = default;
 
   ~Error() = default;
 
@@ -236,8 +297,9 @@ private:
 
   // Private constructor - use static factory functions instead!
   Error(ErrorCode code, std::string msg)
-      : state_(new ErrorState(code, std::move(msg))) {}
+      : ok_(false), state_(new ErrorState(code, std::move(msg))) {}
 
+  bool ok_;
   std::unique_ptr<ErrorState> state_;
 };
 
diff --git a/cpp/fory/util/error_test.cc b/cpp/fory/util/error_test.cc
index a6ac2e891..83256d064 100644
--- a/cpp/fory/util/error_test.cc
+++ b/cpp/fory/util/error_test.cc
@@ -129,8 +129,10 @@ TEST_F(ErrorTest, ErrorFactories) {
 }
 
 TEST_F(ErrorTest, ErrorSize) {
-  // Error should be the same size as a pointer (unique_ptr)
-  ASSERT_EQ(sizeof(Error), sizeof(void *));
+  // Error contains bool ok_ + unique_ptr<ErrorState> state_
+  // This gives us: 1 byte bool + 7 bytes padding + 8 bytes unique_ptr = 16
+  // bytes on 64-bit systems.
+  ASSERT_EQ(sizeof(Error), sizeof(void *) + sizeof(void *));
 }
 
 } // namespace fory


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

Reply via email to