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]

Reply via email to