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]