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 4b4fafc19 fix(c++): make temporal types hashable (#3789)
4b4fafc19 is described below
commit 4b4fafc19a44f2a82385a954a20748a95ea47372
Author: Peiyang He <[email protected]>
AuthorDate: Mon Jun 29 15:07:45 2026 +0800
fix(c++): make temporal types hashable (#3789)
## Why?
C++ temporal types (date, duration, timestamp) should be hashable so
they can be used as key in `unordered_map`.
## What does this PR do?
1. Define `fory::Timestamp` and `fory::Duration` in
`cpp/fory/type/temporal.h ` as wrapper types for
`std::chrono::time_point` and `std::chrono::nanoseconds`, instead of
their aliases. Inside these wrapper types, conversion APIs to and from
the chrono representations are provided. I also move
`fory::Serialization::Date` into `cpp/fory/type/temporal.h` as
`fory::Date`.
2. Specify `std::hash` for temporal types (date, duration, timestamp).
3. Add serializers for raw `std::chrono::time_point` and
`std::chrono::nanoseconds`.
4. In `type_resolver.h`, reject registering `chrono` temporal types for
`std::any`. chrono nanoseconds and chrono timestamp still work as
explicit target types, but dynamic `std::any` always uses the Fory
carriers.
5. Modify/Add testcases accordingly.
6. Modify the doc accordingly.
## Related issues
Fixes https://github.com/apache/fory/issues/3787.
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence or equivalent persisted links of the
final clean AI review results from both fresh reviewers described in
`AI_POLICY.md`, the Fory-guided reviewer and the independent general
reviewer, on the current PR diff or current HEAD after the latest code
changes.
## Does this PR introduce any user-facing change?
Yes. The changes are documented accordingly.
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
---
.../fory_compiler/tests/test_generated_code.py | 26 ++++
cpp/fory/serialization/serialization_test.cc | 153 ++++++++++++++++++-
cpp/fory/serialization/temporal_serializers.h | 166 +++++++++++++++------
cpp/fory/serialization/type_resolver.h | 49 +++---
cpp/fory/type/temporal.h | 139 +++++++++++++++++
docs/compiler/schema-idl.md | 52 +++----
docs/guide/cpp/supported-types.md | 43 ++++--
docs/guide/cpp/xlang-serialization.md | 10 +-
docs/specification/xlang_type_mapping.md | 9 +-
integration_tests/idl_tests/cpp/main.cc | 129 ++++++++--------
10 files changed, 599 insertions(+), 177 deletions(-)
diff --git a/compiler/fory_compiler/tests/test_generated_code.py
b/compiler/fory_compiler/tests/test_generated_code.py
index a1b08bff5..d5f7b3960 100644
--- a/compiler/fory_compiler/tests/test_generated_code.py
+++ b/compiler/fory_compiler/tests/test_generated_code.py
@@ -1169,6 +1169,32 @@ def
test_cpp_nested_container_ref_uses_correct_pointer_type():
)
+def test_cpp_temporal_map_keys_use_fory_owned_wrappers():
+ schema = parse_fdl(
+ dedent(
+ """
+ package demo;
+
+ message Holder {
+ map<duration, string> durations = 1;
+ map<timestamp, string> timestamps = 2;
+ map<date, string> dates = 3;
+ }
+ """
+ )
+ )
+
+ cpp_output = render_files(generate_files(schema, CppGenerator))
+ assert (
+ "std::unordered_map<fory::serialization::Duration, std::string>" in
cpp_output
+ )
+ assert (
+ "std::unordered_map<fory::serialization::Timestamp, std::string>" in
cpp_output
+ )
+ assert "std::unordered_map<fory::serialization::Date, std::string>" in
cpp_output
+ assert "std::map<" not in cpp_output
+
+
def test_java_enum_generation_uses_fory_enum_ids():
schema = parse_fdl(
dedent(
diff --git a/cpp/fory/serialization/serialization_test.cc
b/cpp/fory/serialization/serialization_test.cc
index 8bed168d6..cda99cfdc 100644
--- a/cpp/fory/serialization/serialization_test.cc
+++ b/cpp/fory/serialization/serialization_test.cc
@@ -22,6 +22,7 @@
#include "fory/serialization/skip.h"
#include "fory/thirdparty/MurmurHash3.h"
#include "gtest/gtest.h"
+#include <any>
#include <array>
#include <atomic>
#include <chrono>
@@ -31,6 +32,8 @@
#include <map>
#include <string>
#include <thread>
+#include <typeinfo>
+#include <unordered_map>
#include <variant>
#include <vector>
@@ -193,6 +196,24 @@ void test_roundtrip(const T &original, bool should_equal =
true) {
}
}
+template <typename T> void expect_any_roundtrip(Fory &fory, const T &value) {
+ std::any original = value;
+ auto serialize_result = fory.serialize(original);
+ ASSERT_TRUE(serialize_result.ok())
+ << "Serialization failed: " << serialize_result.error().to_string();
+
+ auto deserialize_result =
+ fory.deserialize<std::any>(serialize_result.value());
+ ASSERT_TRUE(deserialize_result.ok())
+ << "Deserialization failed: " << deserialize_result.error().to_string();
+
+ std::any decoded = std::move(deserialize_result).value();
+ EXPECT_EQ(decoded.type(), typeid(T));
+ const auto *decoded_value = std::any_cast<T>(&decoded);
+ ASSERT_NE(decoded_value, nullptr);
+ EXPECT_EQ(*decoded_value, value);
+}
+
// ============================================================================
// Primitive Type Tests
// ============================================================================
@@ -370,10 +391,11 @@ TEST(SerializationTest, DurationRoundtrip) {
auto fory =
Fory::builder().xlang(true).compatible(false).track_ref(false).build();
std::vector<Duration> values = {
- Duration(0),
- std::chrono::seconds(12) + Duration(345678901),
- -std::chrono::seconds(7) - std::chrono::milliseconds(45) - Duration(67),
- Duration(-1),
+ Duration(std::chrono::nanoseconds(0)),
+ Duration(std::chrono::seconds(12) + std::chrono::nanoseconds(345678901)),
+ Duration(-std::chrono::seconds(7) - std::chrono::milliseconds(45) -
+ std::chrono::nanoseconds(67)),
+ Duration(std::chrono::nanoseconds(-1)),
};
for (const Duration &original : values) {
@@ -550,9 +572,9 @@ TEST(SerializationTest,
DurationUsesSecondsAndNanosecondsPayload) {
auto fory =
Fory::builder().xlang(true).compatible(false).track_ref(false).build();
std::vector<TestCase> cases = {
- {Duration(1234567890), 1, 234567890},
- {Duration(-1234567890), -1, -234567890},
- {Duration(-1), 0, -1},
+ {Duration(std::chrono::nanoseconds(1234567890)), 1, 234567890},
+ {Duration(std::chrono::nanoseconds(-1234567890)), -1, -234567890},
+ {Duration(std::chrono::nanoseconds(-1)), 0, -1},
};
for (const TestCase &test_case : cases) {
@@ -579,7 +601,8 @@ TEST(SerializationTest,
DurationSkipConsumesSecondsAndNanosecondsPayload) {
auto fory =
Fory::builder().xlang(true).compatible(false).track_ref(false).build();
WriteContext write_ctx(fory.config(), fory.type_resolver().clone());
- Serializer<Duration>::write_data(Duration(-1), write_ctx);
+ Serializer<Duration>::write_data(Duration(std::chrono::nanoseconds(-1)),
+ write_ctx);
ASSERT_FALSE(write_ctx.has_error()) << write_ctx.error().to_string();
ReadContext read_ctx(fory.config(), fory.type_resolver().clone());
@@ -805,6 +828,13 @@ TEST(SerializationTest, MapStringIntRoundtrip) {
std::map<std::string, int32_t>{{"one", 1}, {"two", 2}, {"three", 3}});
}
+TEST(SerializationTest, UnorderedMapStringIntRoundtrip) {
+ test_roundtrip(std::unordered_map<std::string, int32_t>{});
+ test_roundtrip(std::unordered_map<std::string, int32_t>{{"one", 1}});
+ test_roundtrip(std::unordered_map<std::string, int32_t>{
+ {"one", 1}, {"two", 2}, {"three", 3}});
+}
+
TEST(SerializationTest, NestedVectorRoundtrip) {
test_roundtrip(std::vector<std::vector<int32_t>>{});
test_roundtrip(std::vector<std::vector<int32_t>>{{1, 2}, {3, 4}, {5}});
@@ -1489,6 +1519,113 @@ TEST(SerializationTest,
ThreadSafeForyRejectsRegistrationAfterFirstSerialize) {
std::string::npos);
}
+TEST(SerializationTest, TemporalCarriersAreHashable) {
+ std::unordered_map<Date, std::string> date_map;
+ date_map[Date(0)] = "epoch";
+ date_map[Date(18954)] = "future";
+ date_map[Date(-1)] = "past";
+ EXPECT_EQ(date_map.size(), 3u);
+ EXPECT_EQ(date_map[Date(0)], "epoch");
+
+ std::unordered_map<Duration, std::string> dur_map;
+ dur_map[Duration(std::chrono::nanoseconds(0))] = "zero";
+ dur_map[Duration(std::chrono::seconds(1))] = "one_sec";
+ dur_map[Duration(std::chrono::nanoseconds(-1))] = "neg";
+ EXPECT_EQ(dur_map.size(), 3u);
+ EXPECT_EQ(dur_map[Duration(std::chrono::nanoseconds(0))], "zero");
+
+ std::unordered_map<Timestamp, std::string> ts_map;
+ ts_map[Timestamp()] = "epoch";
+ ts_map[Timestamp(std::chrono::nanoseconds(1000000000LL))] = "one_sec";
+ EXPECT_EQ(ts_map.size(), 2u);
+ EXPECT_EQ(ts_map[Timestamp()], "epoch");
+}
+
+TEST(SerializationTest, DurationChronoConversion) {
+ auto ns = std::chrono::seconds(5) + std::chrono::nanoseconds(123);
+ Duration d(ns);
+ EXPECT_EQ(d.to_chrono(), ns);
+ EXPECT_EQ(d.count(), ns.count());
+
+ Duration zero;
+ EXPECT_EQ(zero.count(), 0);
+ EXPECT_EQ(zero.to_chrono(), std::chrono::nanoseconds(0));
+}
+
+TEST(SerializationTest, TimestampChronoConversion) {
+ auto ns = std::chrono::nanoseconds(1700000000000000000LL);
+ Timestamp ts(ns);
+ EXPECT_EQ(ts.time_since_epoch(), ns);
+ EXPECT_EQ(ts.to_chrono().time_since_epoch(), ns);
+
+ Timestamp epoch;
+ EXPECT_EQ(epoch.time_since_epoch().count(), 0);
+}
+
+TEST(SerializationTest, TemporalAnyUsesCanonicalCarriers) {
+ auto fory =
+ Fory::builder().xlang(true).compatible(false).track_ref(false).build();
+ ASSERT_TRUE(register_any_type<Duration>(fory.type_resolver()).ok());
+ ASSERT_TRUE(register_any_type<Timestamp>(fory.type_resolver()).ok());
+
+ expect_any_roundtrip(
+ fory, Duration(std::chrono::seconds(12) + std::chrono::nanoseconds(34)));
+ expect_any_roundtrip(fory,
+ Timestamp(std::chrono::nanoseconds(1234567890123LL)));
+}
+
+TEST(SerializationTest, ChronoTemporalAnyRegistrationIsRejected) {
+ using ChronoTimestamp = std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::nanoseconds>;
+ auto fory =
+ Fory::builder().xlang(true).compatible(false).track_ref(false).build();
+
+ auto chrono_duration_registration =
+ register_any_type<std::chrono::nanoseconds>(fory.type_resolver());
+ ASSERT_FALSE(chrono_duration_registration.ok());
+ EXPECT_NE(chrono_duration_registration.error().to_string().find(
+ "explicit static targets"),
+ std::string::npos);
+
+ ASSERT_TRUE(register_any_type<Duration>(fory.type_resolver()).ok());
+ ASSERT_TRUE(register_any_type<Timestamp>(fory.type_resolver()).ok());
+
+ auto chrono_timestamp_registration =
+ register_any_type<ChronoTimestamp>(fory.type_resolver());
+ ASSERT_FALSE(chrono_timestamp_registration.ok());
+ EXPECT_NE(chrono_timestamp_registration.error().to_string().find(
+ "explicit static targets"),
+ std::string::npos);
+
+ expect_any_roundtrip(fory, Duration(std::chrono::nanoseconds(-1)));
+ expect_any_roundtrip(fory,
Timestamp(std::chrono::nanoseconds(1000000000LL)));
+}
+
+TEST(SerializationTest, ForyOwnedAndChronoShareWireEncoding) {
+ auto fory =
+ Fory::builder().xlang(true).compatible(false).track_ref(false).build();
+
+ auto ns = std::chrono::seconds(7) + std::chrono::nanoseconds(654321);
+ Duration fory_dur(ns);
+ std::chrono::nanoseconds chrono_dur = ns;
+
+ auto fory_bytes = fory.serialize(fory_dur);
+ auto chrono_bytes = fory.serialize(chrono_dur);
+ ASSERT_TRUE(fory_bytes.ok());
+ ASSERT_TRUE(chrono_bytes.ok());
+ EXPECT_EQ(fory_bytes.value(), chrono_bytes.value());
+
+ auto decoded_from_fory = fory.deserialize<std::chrono::nanoseconds>(
+ fory_bytes.value().data(), fory_bytes.value().size());
+ ASSERT_TRUE(decoded_from_fory.ok());
+ EXPECT_EQ(decoded_from_fory.value(), ns);
+
+ auto decoded_from_chrono = fory.deserialize<Duration>(
+ chrono_bytes.value().data(), chrono_bytes.value().size());
+ ASSERT_TRUE(decoded_from_chrono.ok());
+ EXPECT_EQ(decoded_from_chrono.value(), fory_dur);
+}
+
} // namespace test
} // namespace serialization
} // namespace fory
diff --git a/cpp/fory/serialization/temporal_serializers.h
b/cpp/fory/serialization/temporal_serializers.h
index 978809c6b..22527ffb8 100644
--- a/cpp/fory/serialization/temporal_serializers.h
+++ b/cpp/fory/serialization/temporal_serializers.h
@@ -20,50 +20,18 @@
#pragma once
#include "fory/serialization/serializer.h"
+#include "fory/type/temporal.h"
#include <chrono>
#include <limits>
namespace fory {
namespace serialization {
-// ============================================================================
-// Temporal Type Aliases
-// ============================================================================
-
-/// Duration: absolute length of time as nanoseconds
-using Duration = std::chrono::nanoseconds;
-
-/// Timestamp: point in time as nanoseconds since Unix epoch (Jan 1, 1970 UTC)
-using Timestamp = std::chrono::time_point<std::chrono::system_clock,
- std::chrono::nanoseconds>;
-
-/// Date: naive date without timezone as days since Unix epoch
-class Date {
-public:
- Date() : days_since_epoch_(0) {}
- explicit Date(int32_t days) : days_since_epoch_(days) {}
-
- int32_t days_since_epoch() const { return days_since_epoch_; }
-
- bool operator==(const Date &other) const {
- return days_since_epoch_ == other.days_since_epoch_;
- }
-
- bool operator!=(const Date &other) const { return !(*this == other); }
-
- bool operator<(const Date &other) const {
- return days_since_epoch_ < other.days_since_epoch_;
- }
-
-private:
- int32_t days_since_epoch_; // Days since Jan 1, 1970 UTC
-};
-
// ============================================================================
// Duration Serializer
// ============================================================================
-/// Serializer for Duration (std::chrono::nanoseconds)
+/// Serializer for Duration
/// Per xlang spec: serialized as signed varint64 seconds + signed int32
/// nanoseconds
template <> struct Serializer<Duration> {
@@ -95,8 +63,9 @@ template <> struct Serializer<Duration> {
}
static inline void write_data(const Duration &duration, WriteContext &ctx) {
- auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
- auto remainder = duration - seconds;
+ auto ns = duration.to_chrono();
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ns);
+ auto remainder = ns - seconds;
ctx.write_var_int64(seconds.count());
ctx.buffer().write_int32(static_cast<int32_t>(remainder.count()));
}
@@ -110,17 +79,17 @@ template <> struct Serializer<Duration> {
bool read_type) {
bool has_value = read_null_only_flag(ctx, ref_mode);
if (ctx.has_error() || !has_value) {
- return Duration(0);
+ return Duration();
}
if (read_type) {
uint32_t type_id_read = ctx.read_uint8(ctx.error());
if (FORY_PREDICT_FALSE(ctx.has_error())) {
- return Duration(0);
+ return Duration();
}
if (type_id_read != static_cast<uint32_t>(type_id)) {
ctx.set_error(
Error::type_mismatch(type_id_read,
static_cast<uint32_t>(type_id)));
- return Duration(0);
+ return Duration();
}
}
return read_data(ctx);
@@ -129,11 +98,11 @@ template <> struct Serializer<Duration> {
static inline Duration read_data(ReadContext &ctx) {
int64_t seconds = ctx.read_var_int64(ctx.error());
if (FORY_PREDICT_FALSE(ctx.has_error())) {
- return Duration(0);
+ return Duration();
}
int32_t nanos = ctx.read_int32(ctx.error());
- return std::chrono::duration_cast<Duration>(std::chrono::seconds(seconds))
+
- Duration(nanos);
+ return Duration(std::chrono::seconds(seconds) +
+ std::chrono::nanoseconds(nanos));
}
static inline Duration read_with_type_info(ReadContext &ctx, RefMode
ref_mode,
@@ -200,17 +169,17 @@ template <> struct Serializer<Timestamp> {
bool read_type) {
bool has_value = read_null_only_flag(ctx, ref_mode);
if (ctx.has_error() || !has_value) {
- return Timestamp(std::chrono::nanoseconds(0));
+ return Timestamp();
}
if (read_type) {
uint32_t type_id_read = ctx.read_uint8(ctx.error());
if (FORY_PREDICT_FALSE(ctx.has_error())) {
- return Timestamp(std::chrono::nanoseconds(0));
+ return Timestamp();
}
if (type_id_read != static_cast<uint32_t>(type_id)) {
ctx.set_error(
Error::type_mismatch(type_id_read,
static_cast<uint32_t>(type_id)));
- return Timestamp(std::chrono::nanoseconds(0));
+ return Timestamp();
}
}
return read_data(ctx);
@@ -219,7 +188,7 @@ template <> struct Serializer<Timestamp> {
static inline Timestamp read_data(ReadContext &ctx) {
int64_t seconds = ctx.read_int64(ctx.error());
if (FORY_PREDICT_FALSE(ctx.has_error())) {
- return Timestamp(std::chrono::nanoseconds(0));
+ return Timestamp();
}
uint32_t nanos = ctx.read_uint32(ctx.error());
return Timestamp(std::chrono::seconds(seconds) +
@@ -315,5 +284,110 @@ template <> struct Serializer<Date> {
}
};
+// ============================================================================
+// Chrono serializers
+//
+// These allow users to explicitly request chrono types as the deserialization
+// target (e.g., fory.deserialize<std::chrono::nanoseconds>(...)). They share
+// the same wire encoding as the Fory-owned carrier serializers above and
+// delegate to them via conversion
+// ============================================================================
+
+/// Serializer for std::chrono::nanoseconds
+/// Per xlang spec: serialized as signed varint64 seconds + signed int32
+/// nanoseconds
+template <> struct Serializer<std::chrono::nanoseconds> {
+ static constexpr TypeId type_id = TypeId::DURATION;
+
+ static inline void write_type_info(WriteContext &ctx) {
+ ctx.write_uint8(static_cast<uint8_t>(type_id));
+ }
+
+ static inline void read_type_info(ReadContext &ctx) {
+ Serializer<Duration>::read_type_info(ctx);
+ }
+
+ static inline void write(const std::chrono::nanoseconds &ns,
+ WriteContext &ctx, RefMode ref_mode, bool
write_type,
+ bool has_generics = false) {
+ Serializer<Duration>::write(Duration(ns), ctx, ref_mode, write_type,
+ has_generics);
+ }
+
+ static inline void write_data(const std::chrono::nanoseconds &ns,
+ WriteContext &ctx) {
+ Serializer<Duration>::write_data(Duration(ns), ctx);
+ }
+
+ static inline void write_data_generic(const std::chrono::nanoseconds &ns,
+ WriteContext &ctx, bool has_generics) {
+ write_data(ns, ctx);
+ }
+
+ static inline std::chrono::nanoseconds
+ read(ReadContext &ctx, RefMode ref_mode, bool read_type) {
+ return Serializer<Duration>::read(ctx, ref_mode, read_type).to_chrono();
+ }
+
+ static inline std::chrono::nanoseconds read_data(ReadContext &ctx) {
+ return Serializer<Duration>::read_data(ctx).to_chrono();
+ }
+
+ static inline std::chrono::nanoseconds
+ read_with_type_info(ReadContext &ctx, RefMode ref_mode,
+ const TypeInfo &type_info) {
+ return read(ctx, ref_mode, false);
+ }
+};
+
+/// Serializer for std::chrono::time_point
+/// Per xlang spec: serialized as int64 seconds + uint32 nanoseconds since Unix
+/// epoch
+template <>
+struct Serializer<std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::nanoseconds>> {
+ using ChronoTs = std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::nanoseconds>;
+ static constexpr TypeId type_id = TypeId::TIMESTAMP;
+
+ static inline void write_type_info(WriteContext &ctx) {
+ ctx.write_uint8(static_cast<uint8_t>(type_id));
+ }
+
+ static inline void read_type_info(ReadContext &ctx) {
+ Serializer<Timestamp>::read_type_info(ctx);
+ }
+
+ static inline void write(const ChronoTs &tp, WriteContext &ctx,
+ RefMode ref_mode, bool write_type,
+ bool has_generics = false) {
+ Serializer<Timestamp>::write(Timestamp(tp), ctx, ref_mode, write_type,
+ has_generics);
+ }
+
+ static inline void write_data(const ChronoTs &tp, WriteContext &ctx) {
+ Serializer<Timestamp>::write_data(Timestamp(tp), ctx);
+ }
+
+ static inline void write_data_generic(const ChronoTs &tp, WriteContext &ctx,
+ bool has_generics) {
+ write_data(tp, ctx);
+ }
+
+ static inline ChronoTs read(ReadContext &ctx, RefMode ref_mode,
+ bool read_type) {
+ return Serializer<Timestamp>::read(ctx, ref_mode, read_type).to_chrono();
+ }
+
+ static inline ChronoTs read_data(ReadContext &ctx) {
+ return Serializer<Timestamp>::read_data(ctx).to_chrono();
+ }
+
+ static inline ChronoTs read_with_type_info(ReadContext &ctx, RefMode
ref_mode,
+ const TypeInfo &type_info) {
+ return read(ctx, ref_mode, false);
+ }
+};
+
} // namespace serialization
} // namespace fory
diff --git a/cpp/fory/serialization/type_resolver.h
b/cpp/fory/serialization/type_resolver.h
index 1b8eea007..1a14a8570 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -22,6 +22,7 @@
#include <algorithm>
#include <any>
#include <array>
+#include <chrono>
#include <cstdint>
#include <deque>
#include <list>
@@ -1631,27 +1632,39 @@ get_type_info_with_resolver(TypeResolver &resolver) {
template <typename T> Result<void, Error> TypeResolver::register_any_type() {
check_registration_thread();
- constexpr uint32_t static_type_id =
- static_cast<uint32_t>(Serializer<T>::type_id);
- TypeInfo *type_info = nullptr;
- if (is_internal_type(static_type_id)) {
- type_info = type_info_by_id_.get_or_default(static_type_id, nullptr);
- if (FORY_PREDICT_FALSE(type_info == nullptr)) {
- return Unexpected(Error::type_error("TypeInfo not found for type_id: " +
- std::to_string(static_type_id)));
- }
+ using ChronoTimestamp = std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::nanoseconds>;
+ if constexpr (std::is_same_v<T, std::chrono::nanoseconds> ||
+ std::is_same_v<T, ChronoTimestamp>) {
+ // Chrono temporal serializers are explicit static adapters;
+ // Any reads for shared DURATION/TIMESTAMP TypeInfo stay on the Fory
carrier
+ // types
+ return Unexpected(Error::type_error(
+ "Chrono temporal types are explicit static targets and cannot be "
+ "registered for std::any"));
} else {
- constexpr uint64_t ctid = type_index<T>();
- type_info = type_info_by_ctid_.get_or_default(ctid, nullptr);
- if (FORY_PREDICT_FALSE(type_info == nullptr)) {
- return Unexpected(Error::type_error("Type not registered"));
+ constexpr uint32_t static_type_id =
+ static_cast<uint32_t>(Serializer<T>::type_id);
+ TypeInfo *type_info = nullptr;
+ if (is_internal_type(static_type_id)) {
+ type_info = type_info_by_id_.get_or_default(static_type_id, nullptr);
+ if (FORY_PREDICT_FALSE(type_info == nullptr)) {
+ return Unexpected(Error::type_error("TypeInfo not found for type_id: "
+
+ std::to_string(static_type_id)));
+ }
+ } else {
+ constexpr uint64_t ctid = type_index<T>();
+ type_info = type_info_by_ctid_.get_or_default(ctid, nullptr);
+ if (FORY_PREDICT_FALSE(type_info == nullptr)) {
+ return Unexpected(Error::type_error("Type not registered"));
+ }
}
- }
- type_info->harness.any_write_fn = &detail::any_write_adapter<T>;
- type_info->harness.any_read_fn = &detail::any_read_adapter<T>;
- register_type_internal_runtime(std::type_index(typeid(T)), type_info);
- return Result<void, Error>();
+ type_info->harness.any_write_fn = &detail::any_write_adapter<T>;
+ type_info->harness.any_read_fn = &detail::any_read_adapter<T>;
+ register_type_internal_runtime(std::type_index(typeid(T)), type_info);
+ return Result<void, Error>();
+ }
}
template <typename T>
diff --git a/cpp/fory/type/temporal.h b/cpp/fory/type/temporal.h
new file mode 100644
index 000000000..a599fe7e5
--- /dev/null
+++ b/cpp/fory/type/temporal.h
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+
+namespace fory {
+
+/// Duration: absolute length of time as nanoseconds
+class Duration {
+public:
+ Duration() : ns_(0) {}
+
+ explicit Duration(std::chrono::nanoseconds ns) : ns_(ns) {}
+
+ std::chrono::nanoseconds to_chrono() const { return ns_; }
+
+ int64_t count() const { return ns_.count(); }
+
+ bool operator==(const Duration &other) const { return ns_ == other.ns_; }
+
+ bool operator!=(const Duration &other) const { return !(*this == other); }
+
+ bool operator<(const Duration &other) const { return ns_ < other.ns_; }
+
+private:
+ std::chrono::nanoseconds ns_;
+};
+
+/// Timestamp: point in time as nanoseconds since Unix epoch (Jan 1, 1970 UTC)
+class Timestamp {
+public:
+ using ChronoType = std::chrono::time_point<std::chrono::system_clock,
+ std::chrono::nanoseconds>;
+ Timestamp() : tp_() {}
+
+ explicit Timestamp(ChronoType tp) : tp_(tp) {}
+
+ explicit Timestamp(std::chrono::nanoseconds ns) : tp_(ns) {}
+
+ ChronoType to_chrono() const { return tp_; }
+
+ std::chrono::nanoseconds time_since_epoch() const {
+ return tp_.time_since_epoch();
+ }
+
+ bool operator==(const Timestamp &other) const { return tp_ == other.tp_; }
+
+ bool operator!=(const Timestamp &other) const { return !(*this == other); }
+
+ bool operator<(const Timestamp &other) const { return tp_ < other.tp_; }
+
+private:
+ ChronoType tp_;
+};
+
+/// Date: naive date without timezone as days since Unix epoch
+class Date {
+public:
+ Date() : days_since_epoch_(0) {}
+ explicit Date(int32_t days) : days_since_epoch_(days) {}
+
+ int32_t days_since_epoch() const { return days_since_epoch_; }
+
+ bool operator==(const Date &other) const {
+ return days_since_epoch_ == other.days_since_epoch_;
+ }
+
+ bool operator!=(const Date &other) const { return !(*this == other); }
+
+ bool operator<(const Date &other) const {
+ return days_since_epoch_ < other.days_since_epoch_;
+ }
+
+private:
+ int32_t days_since_epoch_; // Days since Jan 1, 1970 UTC
+};
+
+namespace serialization {
+
+// Keep legacy serialization names available with the carrier types
+using Duration = ::fory::Duration;
+using Timestamp = ::fory::Timestamp;
+using Date = ::fory::Date;
+
+} // namespace serialization
+} // namespace fory
+
+// ============================================================================
+// std::hash specializations for Fory temporal carrier types
+// so they can be used as key types in unordered_map
+//
+// Hash inputs use the canonical numeric representation:
+// Date -> days since epoch (int32_t)
+// Duration -> total nanoseconds (int64_t)
+// Timestamp -> epoch nanoseconds (int64_t)
+// ============================================================================
+
+namespace std {
+
+template <> struct hash<fory::Date> {
+ size_t operator()(const fory::Date &d) const noexcept {
+ return hash<int32_t>{}(d.days_since_epoch());
+ }
+};
+
+template <> struct hash<fory::Duration> {
+ size_t operator()(const fory::Duration &d) const noexcept {
+ return hash<int64_t>{}(d.count());
+ }
+};
+
+template <> struct hash<fory::Timestamp> {
+ size_t operator()(const fory::Timestamp &t) const noexcept {
+ return hash<int64_t>{}(t.time_since_epoch().count());
+ }
+};
+
+} // namespace std
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index 633b90f1e..c12135203 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -1311,38 +1311,38 @@ Underscore spellings for integer encoding are not FDL
type names.
##### Date
-| Language | Type | Notes
|
-| --------------------- | --------------------------- |
--------------------------------------------------------------------------- |
-| Java | `java.time.LocalDate` |
|
-| Python | `datetime.date` |
|
-| Go | `time.Time` | Time portion ignored
|
-| Rust | `fory::Date` | Set
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDate` |
-| C++ | `fory::serialization::Date` |
|
-| JavaScript/TypeScript | `Date` |
|
-| Dart | `LocalDate` | Fory package type
|
+| Language | Type | Notes
|
+| --------------------- | --------------------- |
--------------------------------------------------------------------------- |
+| Java | `java.time.LocalDate` |
|
+| Python | `datetime.date` |
|
+| Go | `time.Time` | Time portion ignored
|
+| Rust | `fory::Date` | Set
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDate` |
+| C++ | `fory::Date` |
|
+| JavaScript/TypeScript | `Date` |
|
+| Dart | `LocalDate` | Fory package type
|
##### Timestamp
-| Language | Type | Notes
|
-| --------------------- | -------------------------------- |
-------------------------------------------------------------------------------
|
-| Java | `java.time.Instant` | UTC-based
|
-| Python | `datetime.datetime` |
|
-| Go | `time.Time` |
|
-| Rust | `fory::Timestamp` | Set
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDateTime` |
-| C++ | `fory::serialization::Timestamp` |
|
-| JavaScript/TypeScript | `Date` |
|
-| Dart | `Timestamp` | Fory package type
|
+| Language | Type | Notes
|
+| --------------------- | ------------------- |
-------------------------------------------------------------------------------
|
+| Java | `java.time.Instant` | UTC-based
|
+| Python | `datetime.datetime` |
|
+| Go | `time.Time` |
|
+| Rust | `fory::Timestamp` | Set
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDateTime` |
+| C++ | `fory::Timestamp` |
|
+| JavaScript/TypeScript | `Date` |
|
+| Dart | `Timestamp` | Fory package type
|
##### Duration
-| Language | Type | Notes
|
-| -------- | ------------------------------- |
-------------------------------------------------------------------------- |
-| Java | `java.time.Duration` |
|
-| Python | `datetime.timedelta` |
|
-| Go | `time.Duration` |
|
-| Rust | `fory::Duration` | Set
`rust_use_chrono_temporal_types = true` to generate `chrono::Duration` |
-| C++ | `fory::serialization::Duration` |
|
-| Dart | `Duration` |
|
+| Language | Type | Notes
|
+| -------- | -------------------- |
-------------------------------------------------------------------------- |
+| Java | `java.time.Duration` |
|
+| Python | `datetime.timedelta` |
|
+| Go | `time.Duration` |
|
+| Rust | `fory::Duration` | Set `rust_use_chrono_temporal_types =
true` to generate `chrono::Duration` |
+| C++ | `fory::Duration` |
|
+| Dart | `Duration` |
|
#### Any
diff --git a/docs/guide/cpp/supported-types.md
b/docs/guide/cpp/supported-types.md
index c4dd9f004..a80a69130 100644
--- a/docs/guide/cpp/supported-types.md
+++ b/docs/guide/cpp/supported-types.md
@@ -189,29 +189,48 @@ OptionalInt value = 42;
## Temporal Types
+`fory::Duration`, `fory::Timestamp`, and `fory::Date` are Fory-owned carrier
+types declared by `fory/type/temporal.h`. They support `std::hash` and can be
+used as `std::unordered_map` keys.
+
+FDL/codegen fields and dynamic `std::any` values use these Fory carrier types
by
+default. C++ `std::chrono` temporal types are supported as explicit
+serialization and deserialization targets when the caller asks for those types.
+
### Duration
-`std::chrono::nanoseconds`:
+Signed duration stored as nanoseconds. Construct from any `std::chrono`
+duration that converts to `std::chrono::nanoseconds`, and call `to_chrono()`
+to get the underlying value back:
```cpp
-using Duration = std::chrono::nanoseconds;
-
-Duration d = std::chrono::seconds(30);
+fory::Duration d(std::chrono::seconds(30));
auto bytes = fory.serialize(d).value();
-auto decoded = fory.deserialize<Duration>(bytes).value();
+auto decoded = fory.deserialize<fory::Duration>(bytes).value();
+
+// Convert to/from std::chrono
+std::chrono::nanoseconds ns = decoded.to_chrono();
+int64_t count = decoded.count(); // total nanoseconds
```
### Timestamp
Point in time since Unix epoch:
+Construct from a `fory::Timestamp::ChronoType` time_point or from nanoseconds
+since epoch, and call `to_chrono()` to get the value back:
```cpp
-using Timestamp = std::chrono::time_point<std::chrono::system_clock,
- std::chrono::nanoseconds>;
+using ChronoTs = fory::Timestamp::ChronoType;
+auto now = std::chrono::time_point_cast<std::chrono::nanoseconds>(
+ std::chrono::system_clock::now());
+
+fory::Timestamp ts(now);
+auto bytes = fory.serialize(ts).value();
+auto decoded = fory.deserialize<fory::Timestamp>(bytes).value();
-Timestamp now = std::chrono::system_clock::now();
-auto bytes = fory.serialize(now).value();
-auto decoded = fory.deserialize<Timestamp>(bytes).value();
+// Convert to/from std::chrono
+ChronoTs tp = decoded.to_chrono();
+std::chrono::nanoseconds since_epoch = decoded.time_since_epoch();
```
### Date
@@ -219,10 +238,10 @@ auto decoded = fory.deserialize<Timestamp>(bytes).value();
Days since Unix epoch:
```cpp
-Date date{18628}; // Days since 1970-01-01
+fory::Date date{18628}; // Days since 1970-01-01
auto bytes = fory.serialize(date).value();
-auto decoded = fory.deserialize<Date>(bytes).value();
+auto decoded = fory.deserialize<fory::Date>(bytes).value();
```
## User-Defined Structs
diff --git a/docs/guide/cpp/xlang-serialization.md
b/docs/guide/cpp/xlang-serialization.md
index fc943a7d4..bd439a8b7 100644
--- a/docs/guide/cpp/xlang-serialization.md
+++ b/docs/guide/cpp/xlang-serialization.md
@@ -190,11 +190,11 @@ Use the field metadata DSL's array node when the schema
is dense `array<T>`.
### Temporal Types
-| C++ Type | Java Type | Python Type | Go Type |
-| ----------- | ----------- | --------------- | --------------- |
-| `Timestamp` | `Instant` | `datetime` | `time.Time` |
-| `Duration` | `Duration` | `timedelta` | `time.Duration` |
-| `Date` | `LocalDate` | `datetime.date` | `time.Time` |
+| C++ Type | Java Type | Python Type | Go Type |
+| ----------------- | ----------- | --------------- | --------------- |
+| `fory::Timestamp` | `Instant` | `datetime` | `time.Time` |
+| `fory::Duration` | `Duration` | `timedelta` | `time.Duration` |
+| `fory::Date` | `LocalDate` | `datetime.date` | `time.Time` |
## Field Order Requirements
diff --git a/docs/specification/xlang_type_mapping.md
b/docs/specification/xlang_type_mapping.md
index 1e7976fa0..a3e877710 100644
--- a/docs/specification/xlang_type_mapping.md
+++ b/docs/specification/xlang_type_mapping.md
@@ -90,9 +90,9 @@ FDL spells them as an encoding modifier plus a semantic
integer type.
| named_ext | 32 | pojo/record
| data class | object
| struct/class |
struct | struct
| `[ForyStruct]` class/struct | @ForyStruct struct/class |
@ForyStruct class | case class/class | data
class/class |
| union | 33 | Union
| typing.Union | /
| `std::variant<Ts...>` | /
| tagged union enum
| `[ForyUnion]` ADT record | tagged enum | @ForyUnion
class | ADT enum | sealed class |
| none | 36 | null
| None | null
| `std::monostate` |
nil | `()`
| null | nil | null
| null | null |
-| duration | 37 | Duration
| timedelta | Number
| duration |
Duration | Duration
| TimeSpan | Duration | Duration
| java.time.Duration | kotlin.time.Duration |
-| timestamp | 38 | Instant
| datetime | Number
| std::chrono::nanoseconds |
Time | Timestamp
| DateTime/DateTimeOffset | Date | Timestamp
| java.time.Instant | java.time.Instant |
-| date | 39 | LocalDate
| datetime.date | Date
| fory::serialization::Date |
fory.Date | Date
| DateOnly | LocalDate | LocalDate
| java.time.LocalDate | java.time.LocalDate |
+| duration | 37 | Duration
| timedelta | Number
| fory::Duration |
Duration | Duration
| TimeSpan | Duration | Duration
| java.time.Duration | kotlin.time.Duration |
+| timestamp | 38 | Instant
| datetime | Number
| fory::Timestamp |
Time | Timestamp
| DateTime/DateTimeOffset | Date | Timestamp
| java.time.Instant | java.time.Instant |
+| date | 39 | LocalDate
| datetime.date | Date
| fory::Date |
fory.Date | Date
| DateOnly | LocalDate | LocalDate
| java.time.LocalDate | java.time.LocalDate |
| decimal | 40 | BigDecimal
| Decimal | Decimal
| fory::serialization::Decimal |
fory.Decimal | fory::Decimal
| decimal | Decimal | Decimal
| java.math.BigDecimal | java.math.BigDecimal |
| binary | 41 | byte[]
| bytes | /
| `uint8_t[n]/vector<T>` |
`[n]uint8/[]T` | `Vec<u8>`
| byte[] | Data | Uint8List
| Array[Byte] | ByteArray |
| `array<bool>` (bool_array) | 43 | bool[]
| BoolArray / ndarray(np.bool\_) | BoolArray /
Type.boolArray() | `bool[n]`
| `[n]bool/[]T` | `Vec<bool>`
| bool[] | [Bool] + @ArrayField | BoolList
| Array[Boolean] | BooleanArray |
@@ -127,6 +127,9 @@ Notes:
of the current xlang type-mapping surface.
- Current xlang uses `*_ARRAY` for one-dimensional primitive arrays and nested
`list` for
multi-dimensional arrays.
+- C++ xlang `date`, `timestamp`, and `duration` map to `fory::Date`,
`fory::Timestamp`, and
+ `fory::Duration` for generated schemas and dynamic `std::any` values.
`std::chrono` temporal
+ types are explicit C++ serialization and deserialization targets only.
- Kotlin KSP xlang maps `UByte`, `UShort`, `UInt`, and `ULong` to `uint8`,
`uint16`, `uint32`, and `uint64`. Kotlin primitive and unsigned array
carriers map to dense arrays. `ByteArray` maps to `binary` by default and to
diff --git a/integration_tests/idl_tests/cpp/main.cc
b/integration_tests/idl_tests/cpp/main.cc
index 8e41bd1d5..cb41a987a 100644
--- a/integration_tests/idl_tests/cpp/main.cc
+++ b/integration_tests/idl_tests/cpp/main.cc
@@ -24,12 +24,12 @@
#include <cstdlib>
#include <fstream>
#include <iostream>
-#include <map>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <tuple>
+#include <unordered_map>
#include <variant>
#include <vector>
@@ -471,61 +471,69 @@ struct ExampleMessageArrays {
};
struct ExampleMessageMaps {
- std::map<bool, std::string> string_values_by_bool;
- std::map<int8_t, std::string> string_values_by_int8;
- std::map<int16_t, std::string> string_values_by_int16;
- std::map<int32_t, std::string> string_values_by_fixed_i32;
- std::map<int32_t, std::string> string_values_by_varint_i32;
- std::map<int64_t, std::string> string_values_by_fixed_i64;
- std::map<int64_t, std::string> string_values_by_varint_i64;
- std::map<int64_t, std::string> string_values_by_tagged_i64;
- std::map<uint8_t, std::string> string_values_by_uint8;
- std::map<uint16_t, std::string> string_values_by_uint16;
- std::map<uint32_t, std::string> string_values_by_fixed_u32;
- std::map<uint32_t, std::string> string_values_by_varint_u32;
- std::map<uint64_t, std::string> string_values_by_fixed_u64;
- std::map<uint64_t, std::string> string_values_by_varint_u64;
- std::map<uint64_t, std::string> string_values_by_tagged_u64;
- std::map<std::string, std::string> string_values_by_string;
- std::map<fory::serialization::Timestamp, std::string>
+ std::unordered_map<bool, std::string> string_values_by_bool;
+ std::unordered_map<int8_t, std::string> string_values_by_int8;
+ std::unordered_map<int16_t, std::string> string_values_by_int16;
+ std::unordered_map<int32_t, std::string> string_values_by_fixed_i32;
+ std::unordered_map<int32_t, std::string> string_values_by_varint_i32;
+ std::unordered_map<int64_t, std::string> string_values_by_fixed_i64;
+ std::unordered_map<int64_t, std::string> string_values_by_varint_i64;
+ std::unordered_map<int64_t, std::string> string_values_by_tagged_i64;
+ std::unordered_map<uint8_t, std::string> string_values_by_uint8;
+ std::unordered_map<uint16_t, std::string> string_values_by_uint16;
+ std::unordered_map<uint32_t, std::string> string_values_by_fixed_u32;
+ std::unordered_map<uint32_t, std::string> string_values_by_varint_u32;
+ std::unordered_map<uint64_t, std::string> string_values_by_fixed_u64;
+ std::unordered_map<uint64_t, std::string> string_values_by_varint_u64;
+ std::unordered_map<uint64_t, std::string> string_values_by_tagged_u64;
+ std::unordered_map<std::string, std::string> string_values_by_string;
+ std::unordered_map<fory::serialization::Timestamp, std::string>
string_values_by_timestamp;
- std::map<fory::serialization::Duration, std::string>
+ std::unordered_map<fory::serialization::Duration, std::string>
string_values_by_duration;
- std::map<ExampleState, std::string> string_values_by_enum;
- std::map<std::string, fory::float16_t> float16_values_by_name;
- std::map<std::string, fory::float16_t> maybe_float16_values_by_name;
- std::map<std::string, fory::bfloat16_t> bfloat16_values_by_name;
- std::map<std::string, fory::bfloat16_t> maybe_bfloat16_values_by_name;
- std::map<std::string, std::vector<uint8_t>> bytes_values_by_name;
- std::map<std::string, fory::serialization::Date> date_values_by_name;
- std::map<std::string, fory::serialization::Decimal> decimal_values_by_name;
- std::map<std::string, ExampleLeaf> message_values_by_name;
- std::map<std::string, ExampleLeafUnion> union_values_by_name;
- std::map<std::string, std::vector<uint8_t>> uint8_array_values_by_name;
- std::map<std::string, std::vector<float>> float32_array_values_by_name;
- std::map<std::string, std::vector<int32_t>> int32_array_values_by_name;
- std::map<fory::serialization::Date, std::string> string_values_by_date;
- std::map<std::string, bool> bool_values_by_name;
- std::map<std::string, int8_t> int8_values_by_name;
- std::map<std::string, int16_t> int16_values_by_name;
- std::map<std::string, int32_t> fixed_i32_values_by_name;
- std::map<std::string, int32_t> varint_i32_values_by_name;
- std::map<std::string, int64_t> fixed_i64_values_by_name;
- std::map<std::string, int64_t> varint_i64_values_by_name;
- std::map<std::string, int64_t> tagged_i64_values_by_name;
- std::map<std::string, uint8_t> uint8_values_by_name;
- std::map<std::string, uint16_t> uint16_values_by_name;
- std::map<std::string, uint32_t> fixed_u32_values_by_name;
- std::map<std::string, uint32_t> varint_u32_values_by_name;
- std::map<std::string, uint64_t> fixed_u64_values_by_name;
- std::map<std::string, uint64_t> varint_u64_values_by_name;
- std::map<std::string, uint64_t> tagged_u64_values_by_name;
- std::map<std::string, float> float32_values_by_name;
- std::map<std::string, double> float64_values_by_name;
- std::map<std::string, fory::serialization::Timestamp>
+ std::unordered_map<ExampleState, std::string> string_values_by_enum;
+ std::unordered_map<std::string, fory::float16_t> float16_values_by_name;
+ std::unordered_map<std::string, fory::float16_t>
maybe_float16_values_by_name;
+ std::unordered_map<std::string, fory::bfloat16_t> bfloat16_values_by_name;
+ std::unordered_map<std::string, fory::bfloat16_t>
+ maybe_bfloat16_values_by_name;
+ std::unordered_map<std::string, std::vector<uint8_t>> bytes_values_by_name;
+ std::unordered_map<std::string, fory::serialization::Date>
+ date_values_by_name;
+ std::unordered_map<std::string, fory::serialization::Decimal>
+ decimal_values_by_name;
+ std::unordered_map<std::string, ExampleLeaf> message_values_by_name;
+ std::unordered_map<std::string, ExampleLeafUnion> union_values_by_name;
+ std::unordered_map<std::string, std::vector<uint8_t>>
+ uint8_array_values_by_name;
+ std::unordered_map<std::string, std::vector<float>>
+ float32_array_values_by_name;
+ std::unordered_map<std::string, std::vector<int32_t>>
+ int32_array_values_by_name;
+ std::unordered_map<fory::serialization::Date, std::string>
+ string_values_by_date;
+ std::unordered_map<std::string, bool> bool_values_by_name;
+ std::unordered_map<std::string, int8_t> int8_values_by_name;
+ std::unordered_map<std::string, int16_t> int16_values_by_name;
+ std::unordered_map<std::string, int32_t> fixed_i32_values_by_name;
+ std::unordered_map<std::string, int32_t> varint_i32_values_by_name;
+ std::unordered_map<std::string, int64_t> fixed_i64_values_by_name;
+ std::unordered_map<std::string, int64_t> varint_i64_values_by_name;
+ std::unordered_map<std::string, int64_t> tagged_i64_values_by_name;
+ std::unordered_map<std::string, uint8_t> uint8_values_by_name;
+ std::unordered_map<std::string, uint16_t> uint16_values_by_name;
+ std::unordered_map<std::string, uint32_t> fixed_u32_values_by_name;
+ std::unordered_map<std::string, uint32_t> varint_u32_values_by_name;
+ std::unordered_map<std::string, uint64_t> fixed_u64_values_by_name;
+ std::unordered_map<std::string, uint64_t> varint_u64_values_by_name;
+ std::unordered_map<std::string, uint64_t> tagged_u64_values_by_name;
+ std::unordered_map<std::string, float> float32_values_by_name;
+ std::unordered_map<std::string, double> float64_values_by_name;
+ std::unordered_map<std::string, fory::serialization::Timestamp>
timestamp_values_by_name;
- std::map<std::string, fory::serialization::Duration> duration_values_by_name;
- std::map<std::string, ExampleState> enum_values_by_name;
+ std::unordered_map<std::string, fory::serialization::Duration>
+ duration_values_by_name;
+ std::unordered_map<std::string, ExampleState> enum_values_by_name;
FORY_STRUCT(
ExampleMessageMaps,
@@ -831,7 +839,8 @@ example_peer::ExampleMessage BuildExampleMessage() {
static_cast<uint8_t>(3)};
message.date_value = Date(19756);
message.timestamp_value = ts(1706933106);
- message.duration_value = std::chrono::seconds(42) + Duration(7000);
+ message.duration_value =
+ Duration(std::chrono::seconds(42) + std::chrono::nanoseconds(7000));
message.decimal_value = Decimal::from_int64(12345, 2);
message.enum_value = example_peer::ExampleState::READY;
message.message_value = leaf;
@@ -864,8 +873,8 @@ example_peer::ExampleMessage BuildExampleMessage() {
{static_cast<uint8_t>(6), static_cast<uint8_t>(7)}};
message.date_list = {Date(19723), Date(19724)};
message.timestamp_list = {ts(1704067200), ts(1704153600)};
- message.duration_list = {std::chrono::milliseconds(1),
- std::chrono::seconds(2)};
+ message.duration_list = {Duration(std::chrono::milliseconds(1)),
+ Duration(std::chrono::seconds(2))};
message.decimal_list = {Decimal::from_int64(125, 2),
Decimal::from_int64(250, 2)};
message.enum_list = {example_peer::ExampleState::UNKNOWN,
@@ -909,7 +918,8 @@ example_peer::ExampleMessage BuildExampleMessage() {
message.string_values_by_tagged_u64 = {{9876543212ULL, "tagged-u64"}};
message.string_values_by_string = {{"name", "value"}};
message.string_values_by_timestamp = {{ts(1709528767), "time"}};
- message.string_values_by_duration = {{std::chrono::seconds(9), "duration"}};
+ message.string_values_by_duration = {
+ {Duration(std::chrono::seconds(9)), "duration"}};
message.string_values_by_enum = {
{example_peer::ExampleState::READY, "ready"}};
message.float16_values_by_name = {{"f16", f16(1.25F)}};
@@ -946,7 +956,8 @@ example_peer::ExampleMessage BuildExampleMessage() {
message.float32_values_by_name = {{"float32", 3.25F}};
message.float64_values_by_name = {{"float64", 6.5}};
message.timestamp_values_by_name = {{"timestamp", ts(1717747750)}};
- message.duration_values_by_name = {{"duration", std::chrono::seconds(10)}};
+ message.duration_values_by_name = {
+ {"duration", Duration(std::chrono::seconds(10))}};
message.enum_values_by_name = {{"enum", example_peer::ExampleState::FAILED}};
return message;
}
@@ -1063,7 +1074,7 @@ fory::Result<void, fory::Error> RunEvolvingRoundTrip() {
return fory::Result<void, fory::Error>();
}
-using StringMap = std::map<std::string, std::string>;
+using StringMap = std::unordered_map<std::string, std::string>;
fory::Result<void, fory::Error> RunRoundTrip(bool compatible) {
auto fory = fory::serialization::Fory::builder()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]