You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2026/04/21 08:18:41 UTC

(fory) branch main updated: chore(dart): refine dart xlang serialization (#3596)

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 1de178f21 chore(dart): refine dart xlang serialization (#3596)
1de178f21 is described below

commit 1de178f21dba979ffb972e4b67f1858648828bb5
Author: Shawn Yang <sh...@gmail.com>
AuthorDate: Tue Apr 21 16:18:36 2026 +0800

    chore(dart): refine dart xlang serialization (#3596)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    - comprehensive tests
    - align time serializers
    - refactor fixed int support
    - add bfloat16/float16 support
    
    
    ## Related issues
    
    #1194
    #1017
    
    ## 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 of the final clean AI review results
    from both fresh reviewers on the current PR diff or current HEAD after
    the latest code changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .../fory-test/lib/entity/xlang_test_manual.dart    |  14 +-
 .../fory-test/lib/entity/xlang_test_models.dart    |  32 +-
 dart/packages/fory/README.md                       |  83 ++--
 dart/packages/fory/lib/fory.dart                   |   9 +-
 dart/packages/fory/lib/src/buffer.dart             |  16 +-
 .../fory/lib/src/codegen/fory_generator.dart       | 191 ++++++---
 .../fory/lib/src/codegen/generated_support.dart    |  84 +++-
 .../fory/lib/src/context/read_context.dart         |  27 +-
 .../fory/lib/src/context/write_context.dart        |  21 +
 dart/packages/fory/lib/src/fory.dart               |   6 +
 dart/packages/fory/lib/src/meta/type_ids.dart      |  15 +-
 dart/packages/fory/lib/src/meta/type_meta.dart     |   4 +
 .../fory/lib/src/resolver/type_resolver.dart       | 139 +++++--
 .../lib/src/serializer/collection_serializers.dart |  26 +-
 .../fory/lib/src/serializer/map_serializers.dart   |  28 +-
 .../lib/src/serializer/primitive_serializers.dart  |  74 ++--
 .../lib/src/serializer/scalar_serializers.dart     |  58 +--
 .../lib/src/serializer/serializer_support.dart     |  50 ++-
 .../fory/lib/src/serializer/struct_serializer.dart |  74 +---
 .../fory/lib/src/serializer/time_serializers.dart  | 220 ++++++++++
 .../src/serializer/typed_array_serializers.dart    |  54 ++-
 .../fory/lib/src/serializer/union_serializer.dart  |  33 ++
 dart/packages/fory/lib/src/types/bfloat16.dart     | 286 +++++++++++++
 dart/packages/fory/lib/src/types/fixed_ints.dart   |  88 ----
 dart/packages/fory/lib/src/types/float16.dart      | 196 ++++++++-
 dart/packages/fory/lib/src/types/float32.dart      | 114 +++++-
 dart/packages/fory/lib/src/types/int16.dart        | 145 +++++++
 dart/packages/fory/lib/src/types/int32.dart        | 145 +++++++
 dart/packages/fory/lib/src/types/int8.dart         | 160 ++++++++
 dart/packages/fory/lib/src/types/timestamp.dart    |   8 +-
 dart/packages/fory/lib/src/types/uint16.dart       | 145 +++++++
 dart/packages/fory/lib/src/types/uint32.dart       | 145 +++++++
 dart/packages/fory/lib/src/types/uint64.dart       | 147 +++++++
 dart/packages/fory/lib/src/types/uint8.dart        | 145 +++++++
 dart/packages/fory/test/buffer_test.dart           | 450 +++++++++++++++++++--
 .../fory/test/collection_serializer_test.dart      | 309 ++++++++++++++
 .../fory/test/enum_union_serializer_test.dart      | 275 +++++++++++++
 dart/packages/fory/test/numeric_wrapper_test.dart  | 320 +++++++++++++++
 .../object_and_compatible_serializer_test.dart     | 298 ++++++++++++++
 .../fory/test/runtime_validation_test.dart         | 430 ++++++++++++++++++++
 .../scalar_and_typed_array_serializer_test.dart    | 423 +++++++++++++++++++
 .../packages/fory/test/string_serializer_test.dart | 311 ++++++++++++++
 dart/packages/fory/test/time_serializer_test.dart  | 386 ++++++++++++++++++
 .../fory/test/unsigned_serializer_test.dart        | 244 +++++++++++
 dart/packages/fory/test/xlang_protocol_test.dart   |  90 +++++
 docs/guide/dart/cross-language.md                  |   7 +-
 docs/guide/dart/index.md                           |   7 +-
 docs/guide/dart/supported-types.md                 |  14 +-
 docs/specification/xlang_type_mapping.md           |   4 +-
 49 files changed, 6087 insertions(+), 463 deletions(-)

diff --git a/dart/packages/fory-test/lib/entity/xlang_test_manual.dart b/dart/packages/fory-test/lib/entity/xlang_test_manual.dart
index 122ced944..d9a839ea0 100644
--- a/dart/packages/fory-test/lib/entity/xlang_test_manual.dart
+++ b/dart/packages/fory-test/lib/entity/xlang_test_manual.dart
@@ -70,17 +70,13 @@ final class _Union2Serializer extends UnionSerializer<Union2> {
   const _Union2Serializer();
 
   @override
-  void write(WriteContext context, Union2 value) {
-    final buffer = context.buffer;
-    buffer.writeVarUint32(value.index);
-    context.writeRef(value.value);
-  }
+  int caseId(Union2 value) => value.index;
+
+  @override
+  Object caseValue(Union2 value) => value.value;
 
   @override
-  Union2 read(ReadContext context) {
-    final buffer = context.buffer;
-    final index = buffer.readVarUint32();
-    final value = context.readRef();
+  Union2 buildValue(int index, Object? value) {
     if (index == 0 && value is String) {
       return Union2.ofString(value);
     }
diff --git a/dart/packages/fory-test/lib/entity/xlang_test_models.dart b/dart/packages/fory-test/lib/entity/xlang_test_models.dart
index 342b31bf4..52fd9303f 100644
--- a/dart/packages/fory-test/lib/entity/xlang_test_models.dart
+++ b/dart/packages/fory-test/lib/entity/xlang_test_models.dart
@@ -440,14 +440,14 @@ class CircularRefStruct {
 class UnsignedSchemaConsistent {
   UnsignedSchemaConsistent();
 
-  UInt8 u8Field = UInt8(0);
-  UInt16 u16Field = UInt16(0);
+  Uint8 u8Field = Uint8(0);
+  Uint16 u16Field = Uint16(0);
 
   @Uint32Type(compress: true)
-  UInt32 u32VarField = UInt32(0);
+  Uint32 u32VarField = Uint32(0);
 
   @Uint32Type(compress: false)
-  UInt32 u32FixedField = UInt32(0);
+  Uint32 u32FixedField = Uint32(0);
 
   @Uint64Type(encoding: LongEncoding.varint)
   int u64VarField = 0;
@@ -459,18 +459,18 @@ class UnsignedSchemaConsistent {
   int u64TaggedField = 0;
 
   @ForyField(nullable: true)
-  UInt8? u8NullableField;
+  Uint8? u8NullableField;
 
   @ForyField(nullable: true)
-  UInt16? u16NullableField;
+  Uint16? u16NullableField;
 
   @ForyField(nullable: true)
   @Uint32Type(compress: true)
-  UInt32? u32VarNullableField;
+  Uint32? u32VarNullableField;
 
   @ForyField(nullable: true)
   @Uint32Type(compress: false)
-  UInt32? u32FixedNullableField;
+  Uint32? u32FixedNullableField;
 
   @ForyField(nullable: true)
   @Uint64Type(encoding: LongEncoding.varint)
@@ -502,18 +502,18 @@ class UnsignedSchemaCompatible {
   UnsignedSchemaCompatible();
 
   @ForyField(nullable: true)
-  UInt8? u8Field1;
+  Uint8? u8Field1;
 
   @ForyField(nullable: true)
-  UInt16? u16Field1;
+  Uint16? u16Field1;
 
   @ForyField(nullable: true)
   @Uint32Type(compress: true)
-  UInt32? u32VarField1;
+  Uint32? u32VarField1;
 
   @ForyField(nullable: true)
   @Uint32Type(compress: false)
-  UInt32? u32FixedField1;
+  Uint32? u32FixedField1;
 
   @ForyField(nullable: true)
   @Uint64Type(encoding: LongEncoding.varint)
@@ -527,14 +527,14 @@ class UnsignedSchemaCompatible {
   @Uint64Type(encoding: LongEncoding.tagged)
   int? u64TaggedField1;
 
-  UInt8 u8Field2 = UInt8(0);
-  UInt16 u16Field2 = UInt16(0);
+  Uint8 u8Field2 = Uint8(0);
+  Uint16 u16Field2 = Uint16(0);
 
   @Uint32Type(compress: true)
-  UInt32 u32VarField2 = UInt32(0);
+  Uint32 u32VarField2 = Uint32(0);
 
   @Uint32Type(compress: false)
-  UInt32 u32FixedField2 = UInt32(0);
+  Uint32 u32FixedField2 = Uint32(0);
 
   @Uint64Type(encoding: LongEncoding.varint)
   int u64VarField2 = 0;
diff --git a/dart/packages/fory/README.md b/dart/packages/fory/README.md
index 1f19a6038..ee4f453c7 100644
--- a/dart/packages/fory/README.md
+++ b/dart/packages/fory/README.md
@@ -13,8 +13,9 @@ cases.
 - Compatible mode for schema evolution
 - Optional reference tracking for shared and circular object graphs
 - Manual serializers for external types, custom payloads, and unions
-- Explicit xlang value wrappers such as `Int32`, `UInt32`, `Float16`,
-  `Float32`, `LocalDate`, and `Timestamp`
+- Explicit xlang value wrappers such as `Int32`, `Uint32`, `Float16`,
+  `Bfloat16`, `Float32`, `LocalDate`, and `Timestamp`, plus `Duration`
+  support
 
 ## Getting Started
 
@@ -219,37 +220,47 @@ void main() {
 
 ## Type Mapping
 
-Dart has no native fixed-width 8/16/32-bit integer or single-precision float
-types. Fory Dart provides thin wrapper types (`Int8`, `Int16`, `Int32`, `UInt8`,
-`UInt16`, `UInt32`, `Float16`, `Float32`) imported from `package:fory/fory.dart`
-to represent these xlang wire types.
-
-| Fory xlang type | Dart type                |
-| --------------- | ------------------------ |
-| bool            | `bool`                   |
-| int8            | `fory.Int8` (wrapper)    |
-| int16           | `fory.Int16` (wrapper)   |
-| int32           | `fory.Int32` (wrapper)   |
-| int64           | `int`                    |
-| float16         | `fory.Float16` (wrapper) |
-| float32         | `fory.Float32` (wrapper) |
-| float64         | `double`                 |
-| string          | `String`                 |
-| binary          | `Uint8List`              |
-| local_date      | `LocalDate`              |
-| timestamp       | `Timestamp`              |
-| list            | `List`                   |
-| set             | `Set`                    |
-| map             | `Map`                    |
-| enum            | `enum`                   |
-| named_struct    | `class`                  |
-| bool_array      | `List<bool>`             |
-| int8_array      | `Int8List`               |
-| int16_array     | `Int16List`              |
-| int32_array     | `Int32List`              |
-| int64_array     | `Int64List`              |
-| float32_array   | `Float32List`            |
-| float64_array   | `Float64List`            |
+Dart has no native fixed-width 8/16/32-bit integer, unsigned 64-bit integer,
+or single-precision float types. Fory Dart provides thin wrapper types
+(`Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`, `Uint64`, `Float16`,
+`Bfloat16`, `Float32`) imported from `package:fory/fory.dart` to represent
+these xlang wire types. For 16-bit floating-point arrays, Dart exposes
+`Float16List` and `Bfloat16List` as contiguous fixed-length buffers.
+
+| Fory xlang type | Dart type                 |
+| --------------- | ------------------------- |
+| bool            | `bool`                    |
+| int8            | `fory.Int8` (wrapper)     |
+| int16           | `fory.Int16` (wrapper)    |
+| int32           | `fory.Int32` (wrapper)    |
+| int64           | `int`                     |
+| uint8           | `fory.Uint8` (wrapper)    |
+| uint16          | `fory.Uint16` (wrapper)   |
+| uint32          | `fory.Uint32` (wrapper)   |
+| uint64          | `fory.Uint64` (wrapper)   |
+| float16         | `fory.Float16` (wrapper)  |
+| bfloat16        | `fory.Bfloat16` (wrapper) |
+| float32         | `fory.Float32` (wrapper)  |
+| float64         | `double`                  |
+| string          | `String`                  |
+| binary          | `Uint8List`               |
+| duration        | `Duration`                |
+| local_date      | `LocalDate`               |
+| timestamp       | `Timestamp`               |
+| list            | `List`                    |
+| set             | `Set`                     |
+| map             | `Map`                     |
+| enum            | `enum`                    |
+| named_struct    | `class`                   |
+| bool_array      | `List<bool>`              |
+| int8_array      | `Int8List`                |
+| int16_array     | `Int16List`               |
+| int32_array     | `Int32List`               |
+| int64_array     | `Int64List`               |
+| float16_array   | `Float16List`             |
+| bfloat16_array  | `Bfloat16List`            |
+| float32_array   | `Float32List`             |
+| float64_array   | `Float64List`             |
 
 ## Public API
 
@@ -265,9 +276,9 @@ The main exported API includes:
   annotations
 - `Int32Type`, `Int64Type`, `Uint32Type`, `Uint64Type` — numeric encoding
   overrides
-- Numeric wrappers: `Int8`, `Int16`, `Int32`, `UInt8`, `UInt16`, `UInt32`,
-  `Float16`, `Float32`
-- Temporal wrappers: `LocalDate`, `Timestamp`
+- Numeric wrappers: `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`,
+  `Uint64`, `Float16`, `Bfloat16`, `Float32`
+- Temporal types: `LocalDate`, `Timestamp`, `Duration`
 
 ## Cross-Language Notes
 
diff --git a/dart/packages/fory/lib/fory.dart b/dart/packages/fory/lib/fory.dart
index 865a9c863..67cc27ab0 100644
--- a/dart/packages/fory/lib/fory.dart
+++ b/dart/packages/fory/lib/fory.dart
@@ -49,8 +49,15 @@ export 'src/meta/type_ids.dart';
 export 'src/serializer/enum_serializer.dart';
 export 'src/serializer/serializer.dart';
 export 'src/serializer/union_serializer.dart';
-export 'src/types/fixed_ints.dart';
+export 'src/types/bfloat16.dart';
 export 'src/types/float16.dart';
 export 'src/types/float32.dart';
+export 'src/types/int16.dart';
+export 'src/types/int32.dart';
+export 'src/types/int8.dart';
 export 'src/types/local_date.dart';
 export 'src/types/timestamp.dart';
+export 'src/types/uint16.dart';
+export 'src/types/uint32.dart';
+export 'src/types/uint64.dart';
+export 'src/types/uint8.dart';
diff --git a/dart/packages/fory/lib/src/buffer.dart b/dart/packages/fory/lib/src/buffer.dart
index dc7af67cc..ee1cdb49a 100644
--- a/dart/packages/fory/lib/src/buffer.dart
+++ b/dart/packages/fory/lib/src/buffer.dart
@@ -22,6 +22,7 @@ import 'dart:typed_data';
 
 import 'package:meta/meta.dart';
 
+import 'package:fory/src/types/bfloat16.dart';
 import 'package:fory/src/types/float16.dart';
 
 final BigInt _mask64Big = (BigInt.one << 64) - BigInt.one;
@@ -255,6 +256,12 @@ final class Buffer {
   /// Reads a half-precision floating-point value.
   Float16 readFloat16() => Float16.fromBits(readUint16());
 
+  /// Writes a bfloat16 floating-point value.
+  void writeBfloat16(Bfloat16 value) => writeUint16(value.toBits());
+
+  /// Reads a bfloat16 floating-point value.
+  Bfloat16 readBfloat16() => Bfloat16.fromBits(readUint16());
+
   /// Writes [value] verbatim.
   void writeBytes(List<int> value) {
     ensureWritable(value.length);
@@ -409,13 +416,12 @@ final class Buffer {
 
   /// Writes a tagged unsigned 64-bit integer.
   void writeTaggedUint64(int value) {
-    final unsigned = _toUnsigned64(value);
-    if (unsigned <= 0x7fffffff) {
-      writeInt32((unsigned << 1) & 0xffffffff);
+    if (value >= 0 && value <= 0x7fffffff) {
+      writeInt32(value << 1);
       return;
     }
     writeUint8(0x01);
-    writeUint64(unsigned);
+    writeUint64(value);
   }
 
   /// Reads an unsigned 64-bit integer written by [writeTaggedUint64].
@@ -487,8 +493,6 @@ Uint8List bufferBytes(Buffer buffer) => buffer._bytes;
 @internal
 ByteData bufferByteData(Buffer buffer) => buffer._view;
 
-int _toUnsigned64(int value) => (BigInt.from(value) & _mask64Big).toInt();
-
 extension on Buffer {
   void _writeVarUint64BigInt(BigInt value) {
     var remaining = value & _mask64Big;
diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart
index 03e6b3384..86a22e905 100644
--- a/dart/packages/fory/lib/src/codegen/fory_generator.dart
+++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart
@@ -89,12 +89,11 @@ final class ForyGenerator extends Generator {
     final generatedApiName = '${helperBaseName}Fory';
 
     final enumSpecs = enumElements.map(_analyzeEnum).toList(growable: false);
-    final structSpecs = annotatedClasses
-        .map(_analyzeStruct)
-        .toList(growable: false);
+    final structSpecs =
+        annotatedClasses.map(_analyzeStruct).toList(growable: false);
     final output = StringBuffer()
       ..writeln(
-        '// ignore_for_file: implementation_imports, invalid_use_of_internal_member, unused_element, unnecessary_null_comparison',
+        '// ignore_for_file: implementation_imports, invalid_use_of_internal_member, no_leading_underscores_for_local_identifiers, unused_element, unused_element_parameter, unnecessary_null_comparison',
       )
       ..writeln();
 
@@ -215,9 +214,8 @@ final class ForyGenerator extends Generator {
     }
     if (_isList(type) || _isSet(type)) {
       final argument = (type as InterfaceType).typeArguments.single;
-      final elementSpec = typeSpec is _ListTypeSpecInfo
-          ? typeSpec.element
-          : null;
+      final elementSpec =
+          typeSpec is _ListTypeSpecInfo ? typeSpec.element : null;
       return _GeneratedFieldTypeSpec(
         typeLiteral: _typeReferenceLiteral(type),
         typeId: _typeIdFor(type),
@@ -278,8 +276,7 @@ final class ForyGenerator extends Generator {
     List<_GeneratedFieldSpec> fields,
   ) {
     final unnamedConstructor = element.unnamedConstructor;
-    final hasZeroArgConstructor =
-        unnamedConstructor != null &&
+    final hasZeroArgConstructor = unnamedConstructor != null &&
         !unnamedConstructor.isFactory &&
         unnamedConstructor.parameters.every(
           (parameter) => parameter.isOptional,
@@ -919,9 +916,8 @@ GeneratedFieldType(
       nullable: fieldType.nullable,
       ref: fieldType.ref,
       dynamic: fieldType.dynamic,
-      arguments: fieldType.arguments
-          .map(_fromDebugFieldType)
-          .toList(growable: false),
+      arguments:
+          fieldType.arguments.map(_fromDebugFieldType).toList(growable: false),
     );
   }
 
@@ -1031,13 +1027,33 @@ GeneratedFieldType(
       return 'Map<${_typeCodeString(keyType)}, ${_typeCodeString(valueType)}>.of((($valueExpression as Map)).map((key, value) => MapEntry($convertedKey, $convertedValue)))';
     }
     if (type.isDartCoreInt) {
-      if (fieldType.typeId == TypeIds.int32 ||
-          fieldType.typeId == TypeIds.varInt32) {
-        return '($valueExpression as Int32).value';
+      switch (fieldType.typeId) {
+        case TypeIds.int8:
+          return 'switch ($valueExpression) { int typed => typed, Int8 typed => typed.value, _ => throw StateError(\'Expected int or Int8.\') }';
+        case TypeIds.int16:
+          return 'switch ($valueExpression) { int typed => typed, Int16 typed => typed.value, _ => throw StateError(\'Expected int or Int16.\') }';
+        case TypeIds.int32:
+        case TypeIds.varInt32:
+          return 'switch ($valueExpression) { int typed => typed, Int32 typed => typed.value, _ => throw StateError(\'Expected int or Int32.\') }';
+        case TypeIds.uint8:
+          return 'switch ($valueExpression) { int typed => typed, Uint8 typed => typed.value, _ => throw StateError(\'Expected int or Uint8.\') }';
+        case TypeIds.uint16:
+          return 'switch ($valueExpression) { int typed => typed, Uint16 typed => typed.value, _ => throw StateError(\'Expected int or Uint16.\') }';
+        case TypeIds.uint32:
+        case TypeIds.varUint32:
+          return 'switch ($valueExpression) { int typed => typed, Uint32 typed => typed.value, _ => throw StateError(\'Expected int or Uint32.\') }';
+        case TypeIds.uint64:
+        case TypeIds.varUint64:
+        case TypeIds.taggedUint64:
+          return 'switch ($valueExpression) { int typed => typed, Uint64 typed => typed.value, _ => throw StateError(\'Expected int or Uint64.\') }';
+        default:
+          return '$valueExpression as int';
       }
-      return '$valueExpression as int';
     }
     if (type.isDartCoreDouble) {
+      if (fieldType.typeId == TypeIds.float32) {
+        return 'switch ($valueExpression) { double typed => typed, Float32 typed => typed.value, _ => throw StateError(\'Expected double or Float32.\') }';
+      }
       return '$valueExpression as double';
     }
     if (type.isDartCoreBool) {
@@ -1118,7 +1134,7 @@ GeneratedFieldType(
   }
 
   List<_DirectGeneratedWriteReservationRun>
-  _directGeneratedWriteReservationRuns(List<_GeneratedFieldSpec> fields) {
+      _directGeneratedWriteReservationRuns(List<_GeneratedFieldSpec> fields) {
     final runs = <_DirectGeneratedWriteReservationRun>[];
     int? start;
     var bytes = 0;
@@ -1161,6 +1177,7 @@ GeneratedFieldType(
       case TypeIds.int16:
       case TypeIds.uint16:
       case TypeIds.float16:
+      case TypeIds.bfloat16:
         return 2;
       case TypeIds.int32:
       case TypeIds.uint32:
@@ -1171,6 +1188,8 @@ GeneratedFieldType(
       case TypeIds.uint64:
       case TypeIds.float64:
         return 8;
+      case TypeIds.duration:
+        return 14;
       case TypeIds.timestamp:
         return 12;
       case TypeIds.varInt32:
@@ -1224,6 +1243,8 @@ GeneratedFieldType(
         return 'buffer.writeTaggedUint64(${_directGeneratedScalarExpression(field, valueExpression)})';
       case TypeIds.float16:
         return 'buffer.writeFloat16($valueExpression)';
+      case TypeIds.bfloat16:
+        return 'buffer.writeBfloat16($valueExpression)';
       case TypeIds.float32:
         return 'buffer.writeFloat32(${_directGeneratedScalarExpression(field, valueExpression)})';
       case TypeIds.float64:
@@ -1234,8 +1255,12 @@ GeneratedFieldType(
         return 'writeGeneratedBinaryValue(context, $valueExpression)';
       case TypeIds.date:
         return 'writeGeneratedLocalDateValue(context, $valueExpression)';
+      case TypeIds.duration:
+        return 'writeGeneratedDurationValue(context, $valueExpression)';
       case TypeIds.timestamp:
-        return 'writeGeneratedTimestampValue(context, $valueExpression)';
+        return _isDateTimeType(field.type)
+            ? 'writeGeneratedDateTimeValue(context, $valueExpression)'
+            : 'writeGeneratedTimestampValue(context, $valueExpression)';
       case TypeIds.boolArray:
         return 'writeGeneratedBoolArrayValue(context, $valueExpression)';
       case TypeIds.int8Array:
@@ -1247,6 +1272,7 @@ GeneratedFieldType(
       case TypeIds.uint32Array:
       case TypeIds.uint64Array:
       case TypeIds.float16Array:
+      case TypeIds.bfloat16Array:
       case TypeIds.float32Array:
       case TypeIds.float64Array:
         return 'writeGeneratedFixedArrayValue(context, $valueExpression)';
@@ -1297,14 +1323,20 @@ GeneratedFieldType(
         return '$cursorExpression.writeTaggedUint64(${_directGeneratedScalarExpression(field, valueExpression)})';
       case TypeIds.float16:
         return '$cursorExpression.writeFloat16($valueExpression)';
+      case TypeIds.bfloat16:
+        return '$cursorExpression.writeBfloat16($valueExpression)';
       case TypeIds.float32:
         return '$cursorExpression.writeFloat32(${_directGeneratedScalarExpression(field, valueExpression)})';
       case TypeIds.float64:
         return '$cursorExpression.writeFloat64(${_directGeneratedScalarExpression(field, valueExpression)})';
       case TypeIds.date:
         return '$cursorExpression.writeInt32($valueExpression.toEpochDay())';
+      case TypeIds.duration:
+        return '$cursorExpression.writeVarInt64(generatedDurationWireSeconds($valueExpression)); $cursorExpression.writeInt32(generatedDurationWireNanoseconds($valueExpression))';
       case TypeIds.timestamp:
-        return '$cursorExpression.writeInt64($valueExpression.seconds); $cursorExpression.writeUint32($valueExpression.nanoseconds)';
+        return _isDateTimeType(field.type)
+            ? '$cursorExpression.writeInt64(generatedDateTimeWireSeconds($valueExpression)); $cursorExpression.writeUint32(generatedDateTimeWireNanoseconds($valueExpression))'
+            : '$cursorExpression.writeInt64($valueExpression.seconds); $cursorExpression.writeUint32(generatedTimestampWireNanoseconds($valueExpression))';
       case TypeIds.enumById:
         return _enumCursorWriteExpression(
           field.type,
@@ -1343,27 +1375,35 @@ GeneratedFieldType(
       case TypeIds.uint8:
         return field.type.isDartCoreInt
             ? 'buffer.readUint8()'
-            : 'UInt8(buffer.readUint8())';
+            : 'Uint8(buffer.readUint8())';
       case TypeIds.uint16:
         return field.type.isDartCoreInt
             ? 'buffer.readUint16()'
-            : 'UInt16(buffer.readUint16())';
+            : 'Uint16(buffer.readUint16())';
       case TypeIds.uint32:
         return field.type.isDartCoreInt
             ? 'buffer.readUint32()'
-            : 'UInt32(buffer.readUint32())';
+            : 'Uint32(buffer.readUint32())';
       case TypeIds.varUint32:
         return field.type.isDartCoreInt
             ? 'buffer.readVarUint32()'
-            : 'UInt32(buffer.readVarUint32())';
+            : 'Uint32(buffer.readVarUint32())';
       case TypeIds.uint64:
-        return 'buffer.readUint64()';
+        return field.type.isDartCoreInt
+            ? 'buffer.readUint64()'
+            : 'Uint64(buffer.readUint64())';
       case TypeIds.varUint64:
-        return 'buffer.readVarUint64()';
+        return field.type.isDartCoreInt
+            ? 'buffer.readVarUint64()'
+            : 'Uint64(buffer.readVarUint64())';
       case TypeIds.taggedUint64:
-        return 'buffer.readTaggedUint64()';
+        return field.type.isDartCoreInt
+            ? 'buffer.readTaggedUint64()'
+            : 'Uint64(buffer.readTaggedUint64())';
       case TypeIds.float16:
         return 'buffer.readFloat16()';
+      case TypeIds.bfloat16:
+        return 'buffer.readBfloat16()';
       case TypeIds.float32:
         return field.type.isDartCoreDouble
             ? 'buffer.readFloat32()'
@@ -1376,8 +1416,12 @@ GeneratedFieldType(
         return 'readGeneratedBinaryValue(context)';
       case TypeIds.date:
         return 'readGeneratedLocalDateValue(context)';
+      case TypeIds.duration:
+        return 'readGeneratedDurationValue(context)';
       case TypeIds.timestamp:
-        return 'readGeneratedTimestampValue(context)';
+        return _isDateTimeType(field.type)
+            ? 'readGeneratedDateTimeValue(context)'
+            : 'readGeneratedTimestampValue(context)';
       case TypeIds.boolArray:
         return 'readGeneratedBoolArrayValue(context)';
       case TypeIds.int8Array:
@@ -1391,8 +1435,11 @@ GeneratedFieldType(
       case TypeIds.uint8Array:
         return 'readGeneratedBinaryValue(context)';
       case TypeIds.uint16Array:
-      case TypeIds.float16Array:
         return 'readGeneratedTypedArrayValue<Uint16List>(context, 2, (bytes) => bytes.buffer.asUint16List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 2))';
+      case TypeIds.float16Array:
+        return 'readGeneratedTypedArrayValue<Float16List>(context, 2, (bytes) => Float16List.view(bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 2))';
+      case TypeIds.bfloat16Array:
+        return 'readGeneratedTypedArrayValue<Bfloat16List>(context, 2, (bytes) => Bfloat16List.view(bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 2))';
       case TypeIds.uint32Array:
         return 'readGeneratedTypedArrayValue<Uint32List>(context, 4, (bytes) => bytes.buffer.asUint32List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 4))';
       case TypeIds.uint64Array:
@@ -1438,27 +1485,35 @@ GeneratedFieldType(
       case TypeIds.uint8:
         return field.type.isDartCoreInt
             ? '$cursorExpression.readUint8()'
-            : 'UInt8($cursorExpression.readUint8())';
+            : 'Uint8($cursorExpression.readUint8())';
       case TypeIds.uint16:
         return field.type.isDartCoreInt
             ? '$cursorExpression.readUint16()'
-            : 'UInt16($cursorExpression.readUint16())';
+            : 'Uint16($cursorExpression.readUint16())';
       case TypeIds.uint32:
         return field.type.isDartCoreInt
             ? '$cursorExpression.readUint32()'
-            : 'UInt32($cursorExpression.readUint32())';
+            : 'Uint32($cursorExpression.readUint32())';
       case TypeIds.varUint32:
         return field.type.isDartCoreInt
             ? '$cursorExpression.readVarUint32()'
-            : 'UInt32($cursorExpression.readVarUint32())';
+            : 'Uint32($cursorExpression.readVarUint32())';
       case TypeIds.uint64:
-        return '$cursorExpression.readUint64()';
+        return field.type.isDartCoreInt
+            ? '$cursorExpression.readUint64()'
+            : 'Uint64($cursorExpression.readUint64())';
       case TypeIds.varUint64:
-        return '$cursorExpression.readVarUint64()';
+        return field.type.isDartCoreInt
+            ? '$cursorExpression.readVarUint64()'
+            : 'Uint64($cursorExpression.readVarUint64())';
       case TypeIds.taggedUint64:
-        return '$cursorExpression.readTaggedUint64()';
+        return field.type.isDartCoreInt
+            ? '$cursorExpression.readTaggedUint64()'
+            : 'Uint64($cursorExpression.readTaggedUint64())';
       case TypeIds.float16:
         return '$cursorExpression.readFloat16()';
+      case TypeIds.bfloat16:
+        return '$cursorExpression.readBfloat16()';
       case TypeIds.float32:
         return field.type.isDartCoreDouble
             ? '$cursorExpression.readFloat32()'
@@ -1467,8 +1522,12 @@ GeneratedFieldType(
         return '$cursorExpression.readFloat64()';
       case TypeIds.date:
         return 'LocalDate.fromEpochDay($cursorExpression.readInt32())';
+      case TypeIds.duration:
+        return 'readGeneratedDurationFromWire($cursorExpression.readVarInt64(), $cursorExpression.readInt32())';
       case TypeIds.timestamp:
-        return 'Timestamp($cursorExpression.readInt64(), $cursorExpression.readUint32())';
+        return _isDateTimeType(field.type)
+            ? 'readGeneratedDateTimeFromWire($cursorExpression.readInt64(), $cursorExpression.readUint32())'
+            : 'readGeneratedTimestampFromWire($cursorExpression.readInt64(), $cursorExpression.readUint32())';
       case TypeIds.enumById:
         return _enumCursorReadExpression(field.type, cursorExpression);
       default:
@@ -1513,6 +1572,8 @@ GeneratedFieldType(
     switch (field.fieldType.typeId) {
       case TypeIds.float16:
         return valueExpression;
+      case TypeIds.bfloat16:
+        return valueExpression;
       default:
         return '$valueExpression.value';
     }
@@ -1690,8 +1751,7 @@ GeneratedFieldType(
     if (leftCompressed != rightCompressed) {
       return leftCompressed ? 1 : -1;
     }
-    final sizeCompare =
-        _primitiveSize(right.fieldType.typeId) -
+    final sizeCompare = _primitiveSize(right.fieldType.typeId) -
         _primitiveSize(left.fieldType.typeId);
     if (sizeCompare != 0) {
       return sizeCompare;
@@ -1791,6 +1851,7 @@ GeneratedFieldType(
       case TypeIds.varUint64:
       case TypeIds.taggedUint64:
       case TypeIds.float16:
+      case TypeIds.bfloat16:
       case TypeIds.float32:
       case TypeIds.float64:
         return true;
@@ -1804,6 +1865,7 @@ GeneratedFieldType(
       case TypeIds.string:
       case TypeIds.binary:
       case TypeIds.date:
+      case TypeIds.duration:
       case TypeIds.timestamp:
       case TypeIds.boolArray:
       case TypeIds.int8Array:
@@ -1815,6 +1877,7 @@ GeneratedFieldType(
       case TypeIds.uint32Array:
       case TypeIds.uint64Array:
       case TypeIds.float16Array:
+      case TypeIds.bfloat16Array:
       case TypeIds.float32Array:
       case TypeIds.float64Array:
         return true;
@@ -1844,9 +1907,9 @@ GeneratedFieldType(
         'tagged' => TypeIds.taggedInt64,
         'fixed' => TypeIds.int64,
         _ => throw InvalidGenerationSourceError(
-          'Unsupported Int64Type encoding: $encodingValue.',
-          element: field,
-        ),
+            'Unsupported Int64Type encoding: $encodingValue.',
+            element: field,
+          ),
       };
       return _IntegerAnnotationSpec(typeId: typeId);
     }
@@ -1878,9 +1941,9 @@ GeneratedFieldType(
         'tagged' => TypeIds.taggedUint64,
         'fixed' => TypeIds.uint64,
         _ => throw InvalidGenerationSourceError(
-          'Unsupported Uint64Type encoding: $encodingValue.',
-          element: field,
-        ),
+            'Unsupported Uint64Type encoding: $encodingValue.',
+            element: field,
+          ),
       };
       return _IntegerAnnotationSpec(typeId: typeId);
     }
@@ -1916,9 +1979,8 @@ GeneratedFieldType(
     final options = _readTypeOptions(reader);
     final keyObj = reader.peek('key');
     final valueObj = reader.peek('value');
-    final key = keyObj != null && !keyObj.isNull
-        ? _readTypeSpecObj(keyObj)
-        : null;
+    final key =
+        keyObj != null && !keyObj.isNull ? _readTypeSpecObj(keyObj) : null;
     final value = valueObj != null && !valueObj.isNull
         ? _readTypeSpecObj(valueObj)
         : null;
@@ -1999,6 +2061,10 @@ GeneratedFieldType(
         return TypeIds.uint32Array;
       case 'Uint64List':
         return TypeIds.uint64Array;
+      case 'Float16List':
+        return TypeIds.float16Array;
+      case 'Bfloat16List':
+        return TypeIds.bfloat16Array;
       case 'Float32List':
         return TypeIds.float32Array;
       case 'Float64List':
@@ -2021,20 +2087,27 @@ GeneratedFieldType(
         return TypeIds.int16;
       case 'Int32':
         return TypeIds.varInt32;
-      case 'UInt8':
+      case 'Uint8':
         return TypeIds.uint8;
-      case 'UInt16':
+      case 'Uint16':
         return TypeIds.uint16;
-      case 'UInt32':
+      case 'Uint32':
         return TypeIds.uint32;
+      case 'Uint64':
+        return TypeIds.uint64;
       case 'Float16':
         return TypeIds.float16;
+      case 'Bfloat16':
+        return TypeIds.bfloat16;
       case 'Float32':
         return TypeIds.float32;
       case 'Timestamp':
+      case 'DateTime':
         return TypeIds.timestamp;
       case 'LocalDate':
         return TypeIds.date;
+      case 'Duration':
+        return TypeIds.duration;
       case 'Object':
         return TypeIds.unknown;
       default:
@@ -2156,6 +2229,13 @@ GeneratedFieldType(
   bool _isNullable(DartType type) =>
       type.nullabilitySuffix == NullabilitySuffix.question;
 
+  bool _isDateTimeType(DartType type) {
+    final nonNullable = _withoutNullability(type);
+    return nonNullable is InterfaceType &&
+        nonNullable.element.name == 'DateTime' &&
+        nonNullable.element.library.isDartCore;
+  }
+
   bool _isList(DartType type) => type.isDartCoreList;
 
   bool _isSet(DartType type) =>
@@ -2187,9 +2267,8 @@ GeneratedFieldType(
       if (nonNullable.typeArguments.isEmpty) {
         return baseName;
       }
-      final typeArguments = nonNullable.typeArguments
-          .map(_typeCodeString)
-          .join(', ');
+      final typeArguments =
+          nonNullable.typeArguments.map(_typeCodeString).join(', ');
       return '$baseName<$typeArguments>';
     }
     return nonNullable.getDisplayString();
@@ -2318,9 +2397,9 @@ final class _ConstructorPlan {
   final List<String> postConstructionFieldNames;
 
   const _ConstructorPlan.mutable()
-    : mode = _ConstructorMode.mutable,
-      arguments = const <_ConstructorArgumentSpec>[],
-      postConstructionFieldNames = const <String>[];
+      : mode = _ConstructorMode.mutable,
+        arguments = const <_ConstructorArgumentSpec>[],
+        postConstructionFieldNames = const <String>[];
 
   const _ConstructorPlan.constructor({
     required this.arguments,
diff --git a/dart/packages/fory/lib/src/codegen/generated_support.dart b/dart/packages/fory/lib/src/codegen/generated_support.dart
index d1b0f405f..d22d4f135 100644
--- a/dart/packages/fory/lib/src/codegen/generated_support.dart
+++ b/dart/packages/fory/lib/src/codegen/generated_support.dart
@@ -34,6 +34,7 @@ import 'package:fory/src/serializer/serialization_field_info.dart';
 import 'package:fory/src/serializer/serializer_support.dart';
 import 'package:fory/src/serializer/struct_serializer.dart';
 import 'package:fory/src/serializer/struct_slots.dart';
+import 'package:fory/src/serializer/time_serializers.dart';
 import 'package:fory/src/serializer/typed_array_serializers.dart';
 
 final BigInt _generatedCursorMask64Big = (BigInt.one << 64) - BigInt.one;
@@ -120,6 +121,10 @@ final class GeneratedWriteCursor {
     writeUint16(value.toBits());
   }
 
+  void writeBfloat16(Bfloat16 value) {
+    writeUint16(value.toBits());
+  }
+
   void writeFloat32(double value) {
     _view.setFloat32(_offset, value, Endian.little);
     _offset += 4;
@@ -187,13 +192,12 @@ final class GeneratedWriteCursor {
   }
 
   void writeTaggedUint64(int value) {
-    final unsigned = (BigInt.from(value) & _generatedCursorMask64Big).toInt();
-    if (unsigned <= 0x7fffffff) {
-      writeInt32((unsigned << 1) & 0xffffffff);
+    if (value >= 0 && value <= 0x7fffffff) {
+      writeInt32(value << 1);
       return;
     }
     writeUint8(0x01);
-    writeUint64(unsigned);
+    writeUint64(value);
   }
 
   void _writeVarUint64BigInt(BigInt value) {
@@ -290,6 +294,8 @@ final class GeneratedReadCursor {
 
   Float16 readFloat16() => Float16.fromBits(readUint16());
 
+  Bfloat16 readBfloat16() => Bfloat16.fromBits(readUint16());
+
   double readFloat32() {
     final value = _view.getFloat32(_offset, Endian.little);
     _offset += 4;
@@ -616,18 +622,78 @@ LocalDate readGeneratedLocalDateValue(ReadContext context) {
   return const LocalDateSerializer().read(context);
 }
 
+@internal
+int generatedDurationWireSeconds(Duration value) {
+  return durationWireSeconds(value);
+}
+
+@internal
+int generatedDurationWireNanoseconds(Duration value) {
+  return durationWireNanoseconds(value);
+}
+
+@internal
+Duration readGeneratedDurationFromWire(int seconds, int nanoseconds) {
+  return durationFromWire(seconds, nanoseconds);
+}
+
+@internal
+void writeGeneratedDurationValue(WriteContext context, Duration value) {
+  const DurationSerializer().write(context, value);
+}
+
+@internal
+Duration readGeneratedDurationValue(ReadContext context) {
+  return const DurationSerializer().read(context);
+}
+
+@internal
+int generatedTimestampWireNanoseconds(Timestamp value) {
+  return timestampWireNanoseconds(value);
+}
+
+@internal
+int generatedDateTimeWireSeconds(DateTime value) {
+  return dateTimeWireSeconds(value);
+}
+
+@internal
+int generatedDateTimeWireNanoseconds(DateTime value) {
+  return dateTimeWireNanoseconds(value);
+}
+
+@internal
+Timestamp readGeneratedTimestampFromWire(int seconds, int nanoseconds) {
+  return timestampFromWire(seconds, nanoseconds);
+}
+
+@internal
+DateTime readGeneratedDateTimeFromWire(int seconds, int nanoseconds) {
+  return dateTimeFromWire(seconds, nanoseconds);
+}
+
 @internal
 void writeGeneratedTimestampValue(WriteContext context, Timestamp value) {
   const TimestampSerializer().write(context, value);
 }
 
+@internal
+void writeGeneratedDateTimeValue(WriteContext context, DateTime value) {
+  const DateTimeSerializer().write(context, value);
+}
+
 @internal
 Timestamp readGeneratedTimestampValue(ReadContext context) {
   return const TimestampSerializer().read(context);
 }
 
 @internal
-void writeGeneratedFixedArrayValue(WriteContext context, TypedData value) {
+DateTime readGeneratedDateTimeValue(ReadContext context) {
+  return const DateTimeSerializer().read(context);
+}
+
+@internal
+void writeGeneratedFixedArrayValue(WriteContext context, Object value) {
   writeTypedArrayBytes(context, value);
 }
 
@@ -688,7 +754,10 @@ Object? readGeneratedStructFieldInfoValue(
       !fieldType.ref &&
       !fieldType.nullable) {
     if (fieldType.isPrimitive) {
-      return context.readPrimitiveValue(fieldType.typeId);
+      return convertPrimitiveFieldValue(
+        context.readPrimitiveValue(fieldType.typeId),
+        fieldType,
+      );
     }
     final resolved = fieldDeclaredTypeInfo(context.typeResolver, field)!;
     if (fieldUsesDeclaredType(context.typeResolver, field)) {
@@ -727,8 +796,7 @@ Object readGeneratedStructDirectValue(
   if (fieldUsesDeclaredType(context.typeResolver, field)) {
     resolved = declared;
   } else {
-    resolved =
-        context.readTypeMetaValue(declared.isNamed ? declared : null);
+    resolved = context.readTypeMetaValue(declared.isNamed ? declared : null);
   }
   context.increaseDepth();
   final value = resolved.structSerializer!.readValue(context, resolved);
diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart
index 006a157dc..0d9ef02ec 100644
--- a/dart/packages/fory/lib/src/context/read_context.dart
+++ b/dart/packages/fory/lib/src/context/read_context.dart
@@ -33,8 +33,11 @@ import 'package:fory/src/serializer/primitive_serializers.dart';
 import 'package:fory/src/serializer/scalar_serializers.dart';
 import 'package:fory/src/serializer/serializer.dart';
 import 'package:fory/src/serializer/struct_slots.dart';
+import 'package:fory/src/serializer/time_serializers.dart';
 import 'package:fory/src/serializer/typed_array_serializers.dart';
+import 'package:fory/src/types/bfloat16.dart';
 import 'package:fory/src/types/float16.dart';
+import 'package:fory/src/types/timestamp.dart';
 
 /// Read-side serializer context.
 ///
@@ -99,7 +102,7 @@ final class ReadContext {
       _readTypeMeta(expectedNamedType);
 
   @internal
-  Object readSerializerPayload(
+  Object? readSerializerPayload(
       Serializer<Object?> serializer, TypeInfo resolved,
       {required bool hasCurrentPreservedRef}) {
     final int? sentinelId;
@@ -112,7 +115,7 @@ final class ReadContext {
       sentinelId = null;
       sentinelDepth = null;
     }
-    final value = serializer.read(this) as Object;
+    final value = serializer.read(this);
     if (needsSentinel &&
         _refReader.preservedRefDepth == sentinelDepth &&
         _refReader.hasPreservedRefId &&
@@ -156,6 +159,9 @@ final class ReadContext {
   /// Reads a half-precision floating-point value.
   Float16 readFloat16() => _buffer.readFloat16();
 
+  /// Reads a bfloat16 floating-point value.
+  Bfloat16 readBfloat16() => _buffer.readBfloat16();
+
   /// Reads a single-precision floating-point value.
   double readFloat32() => _buffer.readFloat32();
 
@@ -294,6 +300,8 @@ final class ReadContext {
       return PrimitiveSerializer.readPayload(this, resolved.typeId);
     }
     switch (resolved.typeId) {
+      case TypeIds.none:
+        return null;
       case TypeIds.string:
         return StringSerializer.readPayload(this);
       case TypeIds.binary:
@@ -314,6 +322,10 @@ final class ReadContext {
         return uint32ArraySerializer.read(this);
       case TypeIds.uint64Array:
         return uint64ArraySerializer.read(this);
+      case TypeIds.float16Array:
+        return float16ArraySerializer.read(this);
+      case TypeIds.bfloat16Array:
+        return bfloat16ArraySerializer.read(this);
       case TypeIds.float32Array:
         return float32ArraySerializer.read(this);
       case TypeIds.float64Array:
@@ -324,6 +336,7 @@ final class ReadContext {
           declaredFieldType?.arguments.isEmpty ?? true
               ? null
               : declaredFieldType!.arguments.first,
+          hasPreservedRef: hasPreservedRef,
         );
       case TypeIds.set:
         return SetSerializer.readPayload(
@@ -331,6 +344,7 @@ final class ReadContext {
           declaredFieldType?.arguments.isEmpty ?? true
               ? null
               : declaredFieldType!.arguments.first,
+          hasPreservedRef: hasPreservedRef,
         );
       case TypeIds.map:
         return MapSerializer.readPayload(
@@ -341,11 +355,18 @@ final class ReadContext {
           declaredFieldType == null || declaredFieldType.arguments.length < 2
               ? null
               : declaredFieldType.arguments[1],
+          hasPreservedRef: hasPreservedRef,
         );
       case TypeIds.date:
         return const LocalDateSerializer().read(this);
+      case TypeIds.duration:
+        return const DurationSerializer().read(this);
       case TypeIds.timestamp:
-        return const TimestampSerializer().read(this);
+        if (declaredFieldType?.type == Timestamp ||
+            resolved.type == Timestamp) {
+          return const TimestampSerializer().read(this);
+        }
+        return const DateTimeSerializer().read(this);
       default:
         if (resolved.kind == RegistrationKind.struct) {
           return resolved.structSerializer!.readValue(
diff --git a/dart/packages/fory/lib/src/context/write_context.dart b/dart/packages/fory/lib/src/context/write_context.dart
index 6ad9b1246..2808f031d 100644
--- a/dart/packages/fory/lib/src/context/write_context.dart
+++ b/dart/packages/fory/lib/src/context/write_context.dart
@@ -36,7 +36,9 @@ import 'package:fory/src/serializer/map_serializers.dart';
 import 'package:fory/src/serializer/primitive_serializers.dart';
 import 'package:fory/src/serializer/scalar_serializers.dart';
 import 'package:fory/src/serializer/struct_slots.dart';
+import 'package:fory/src/serializer/time_serializers.dart';
 import 'package:fory/src/serializer/typed_array_serializers.dart';
+import 'package:fory/src/types/bfloat16.dart';
 import 'package:fory/src/types/float16.dart';
 import 'package:fory/src/types/local_date.dart';
 import 'package:fory/src/types/timestamp.dart';
@@ -138,6 +140,9 @@ final class WriteContext {
   /// Writes a half-precision floating-point value.
   void writeFloat16(Float16 value) => _buffer.writeFloat16(value);
 
+  /// Writes a bfloat16 floating-point value.
+  void writeBfloat16(Bfloat16 value) => _buffer.writeBfloat16(value);
+
   /// Writes a single-precision floating-point value.
   void writeFloat32(double value) => _buffer.writeFloat32(value);
 
@@ -270,6 +275,7 @@ final class WriteContext {
       case TypeIds.varUint64:
       case TypeIds.taggedUint64:
       case TypeIds.float16:
+      case TypeIds.bfloat16:
       case TypeIds.float32:
       case TypeIds.float64:
         PrimitiveSerializer.writePayload(this, resolved.typeId, value);
@@ -304,6 +310,12 @@ final class WriteContext {
       case TypeIds.uint64Array:
         uint64ArraySerializer.write(this, value as Uint64List);
         return;
+      case TypeIds.float16Array:
+        float16ArraySerializer.write(this, value as Float16List);
+        return;
+      case TypeIds.bfloat16Array:
+        bfloat16ArraySerializer.write(this, value as Bfloat16List);
+        return;
       case TypeIds.float32Array:
         float32ArraySerializer.write(this, value as Float32List);
         return;
@@ -349,7 +361,16 @@ final class WriteContext {
       case TypeIds.date:
         localDateSerializer.write(this, value as LocalDate);
         return;
+      case TypeIds.duration:
+        durationSerializer.write(this, value as Duration);
+        return;
       case TypeIds.timestamp:
+        if (value is DateTime ||
+            declaredFieldType?.type == DateTime ||
+            resolved.type == DateTime) {
+          dateTimeSerializer.write(this, value as DateTime);
+          return;
+        }
         timestampSerializer.write(this, value as Timestamp);
         return;
       default:
diff --git a/dart/packages/fory/lib/src/fory.dart b/dart/packages/fory/lib/src/fory.dart
index 7d23db095..ca5cffa5c 100644
--- a/dart/packages/fory/lib/src/fory.dart
+++ b/dart/packages/fory/lib/src/fory.dart
@@ -41,6 +41,7 @@ import 'package:fory/src/serializer/serializer.dart';
 final class Fory {
   static const int _nullHeaderFlag = 0x01;
   static const int _xlangHeaderFlag = 0x02;
+  static const int _outOfBandHeaderFlag = 0x04;
 
   late final Buffer _buffer;
   late final WriteContext _writeContext;
@@ -129,6 +130,11 @@ final class Fory {
     _readContext.prepare(buffer);
     try {
       final header = buffer.readUint8();
+      if ((header & _outOfBandHeaderFlag) != 0) {
+        throw StateError(
+          'Out-of-band buffers are not supported by the Dart runtime.',
+        );
+      }
       if ((header & _nullHeaderFlag) != 0) {
         return null as T;
       }
diff --git a/dart/packages/fory/lib/src/meta/type_ids.dart b/dart/packages/fory/lib/src/meta/type_ids.dart
index 08fd2a5c3..b8c0e5c91 100644
--- a/dart/packages/fory/lib/src/meta/type_ids.dart
+++ b/dart/packages/fory/lib/src/meta/type_ids.dart
@@ -36,6 +36,7 @@ abstract final class TypeIds {
   static const int varUint64 = 14;
   static const int taggedUint64 = 15;
   static const int float16 = 17;
+  static const int bfloat16 = 18;
   static const int float32 = 19;
   static const int float64 = 20;
   static const int string = 21;
@@ -54,6 +55,7 @@ abstract final class TypeIds {
   static const int typedUnion = 34;
   static const int namedUnion = 35;
   static const int none = 36;
+  static const int duration = 37;
   static const int timestamp = 38;
   static const int date = 39;
   static const int binary = 41;
@@ -67,6 +69,7 @@ abstract final class TypeIds {
   static const int uint32Array = 50;
   static const int uint64Array = 51;
   static const int float16Array = 53;
+  static const int bfloat16Array = 54;
   static const int float32Array = 55;
   static const int float64Array = 56;
 
@@ -87,6 +90,7 @@ abstract final class TypeIds {
       typeId == varUint64 ||
       typeId == taggedUint64 ||
       typeId == float16 ||
+      typeId == bfloat16 ||
       typeId == float32 ||
       typeId == float64;
 
@@ -108,8 +112,10 @@ abstract final class TypeIds {
 
   static bool isBasicValue(int typeId) =>
       isPrimitive(typeId) ||
+      typeId == none ||
       typeId == string ||
       typeId == binary ||
+      typeId == duration ||
       typeId == timestamp ||
       typeId == date ||
       typeId == boolArray ||
@@ -122,6 +128,7 @@ abstract final class TypeIds {
       typeId == uint32Array ||
       typeId == uint64Array ||
       typeId == float16Array ||
+      typeId == bfloat16Array ||
       typeId == float32Array ||
       typeId == float64Array;
 
@@ -129,7 +136,12 @@ abstract final class TypeIds {
     if (typeId == unknown) {
       return true;
     }
-    if (isPrimitive(typeId) || typeId == binary) {
+    if (isPrimitive(typeId) ||
+        typeId == none ||
+        typeId == binary ||
+        typeId == duration ||
+        typeId == timestamp ||
+        typeId == date) {
       return false;
     }
     switch (typeId) {
@@ -143,6 +155,7 @@ abstract final class TypeIds {
       case uint32Array:
       case uint64Array:
       case float16Array:
+      case bfloat16Array:
       case float32Array:
       case float64Array:
         return false;
diff --git a/dart/packages/fory/lib/src/meta/type_meta.dart b/dart/packages/fory/lib/src/meta/type_meta.dart
index c046b7264..d9829b2cc 100644
--- a/dart/packages/fory/lib/src/meta/type_meta.dart
+++ b/dart/packages/fory/lib/src/meta/type_meta.dart
@@ -259,13 +259,16 @@ final class WireTypeMetaDecoder {
       wireTypeId == TypeIds.varUint64 ||
       wireTypeId == TypeIds.taggedUint64 ||
       wireTypeId == TypeIds.float16 ||
+      wireTypeId == TypeIds.bfloat16 ||
       wireTypeId == TypeIds.float32 ||
       wireTypeId == TypeIds.float64 ||
       wireTypeId == TypeIds.string ||
       wireTypeId == TypeIds.list ||
       wireTypeId == TypeIds.set ||
       wireTypeId == TypeIds.map ||
+      wireTypeId == TypeIds.none ||
       wireTypeId == TypeIds.binary ||
+      wireTypeId == TypeIds.duration ||
       wireTypeId == TypeIds.date ||
       wireTypeId == TypeIds.timestamp ||
       wireTypeId == TypeIds.boolArray ||
@@ -278,6 +281,7 @@ final class WireTypeMetaDecoder {
       wireTypeId == TypeIds.uint32Array ||
       wireTypeId == TypeIds.uint64Array ||
       wireTypeId == TypeIds.float16Array ||
+      wireTypeId == TypeIds.bfloat16Array ||
       wireTypeId == TypeIds.float32Array ||
       wireTypeId == TypeIds.float64Array;
 }
diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart
index 668aee535..f090cf658 100644
--- a/dart/packages/fory/lib/src/resolver/type_resolver.dart
+++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart
@@ -39,13 +39,21 @@ import 'package:fory/src/serializer/scalar_serializers.dart';
 import 'package:fory/src/serializer/serializer.dart';
 import 'package:fory/src/serializer/serialization_field_info.dart';
 import 'package:fory/src/serializer/struct_serializer.dart';
+import 'package:fory/src/serializer/time_serializers.dart';
 import 'package:fory/src/serializer/typed_array_serializers.dart';
 import 'package:fory/src/serializer/union_serializer.dart';
-import 'package:fory/src/types/fixed_ints.dart';
+import 'package:fory/src/types/bfloat16.dart';
 import 'package:fory/src/types/float16.dart';
 import 'package:fory/src/types/float32.dart';
+import 'package:fory/src/types/int16.dart';
+import 'package:fory/src/types/int32.dart';
+import 'package:fory/src/types/int8.dart';
 import 'package:fory/src/types/local_date.dart';
 import 'package:fory/src/types/timestamp.dart';
+import 'package:fory/src/types/uint16.dart';
+import 'package:fory/src/types/uint32.dart';
+import 'package:fory/src/types/uint64.dart';
+import 'package:fory/src/types/uint8.dart';
 import 'package:fory/src/util/hash_util.dart';
 
 enum RegistrationKind { builtin, struct, enumType, ext, union }
@@ -119,7 +127,7 @@ final class TypeResolver {
   final ParsedTypeMetaCache _parsedTypeMetaCache = ParsedTypeMetaCache();
   final List<TypeInfo?> _lastNamedTypeByWireType =
       List<TypeInfo?>.filled(64, null);
-  final List<TypeInfo?> _builtinByTypeId = List<TypeInfo?>.filled(64, null);
+  final Map<_BuiltinKey, TypeInfo> _builtinByKey = <_BuiltinKey, TypeInfo>{};
   final List<_NamedTypeReadCacheEntry?> _namedTypeLookupCache =
       List<_NamedTypeReadCacheEntry?>.filled(128, null);
   final Map<Type, TypeInfo> _runtimeTypeValueCache = <Type, TypeInfo>{};
@@ -317,18 +325,24 @@ final class TypeResolver {
     if (value is int) {
       return _builtin(int, TypeIds.varInt64);
     }
-    if (value is UInt8) {
-      return _builtin(UInt8, TypeIds.uint8);
+    if (value is Uint8) {
+      return _builtin(Uint8, TypeIds.uint8);
     }
-    if (value is UInt16) {
-      return _builtin(UInt16, TypeIds.uint16);
+    if (value is Uint16) {
+      return _builtin(Uint16, TypeIds.uint16);
     }
-    if (value is UInt32) {
-      return _builtin(UInt32, TypeIds.uint32);
+    if (value is Uint32) {
+      return _builtin(Uint32, TypeIds.uint32);
+    }
+    if (value is Uint64) {
+      return _builtin(Uint64, TypeIds.uint64);
     }
     if (value is Float16) {
       return _builtin(Float16, TypeIds.float16);
     }
+    if (value is Bfloat16) {
+      return _builtin(Bfloat16, TypeIds.bfloat16);
+    }
     if (value is Float32) {
       return _builtin(Float32, TypeIds.float32);
     }
@@ -362,6 +376,12 @@ final class TypeResolver {
     if (value is Uint64List) {
       return _builtin(Uint64List, TypeIds.uint64Array);
     }
+    if (value is Float16List) {
+      return _builtin(Float16List, TypeIds.float16Array);
+    }
+    if (value is Bfloat16List) {
+      return _builtin(Bfloat16List, TypeIds.bfloat16Array);
+    }
     if (value is Float32List) {
       return _builtin(Float32List, TypeIds.float32Array);
     }
@@ -383,9 +403,15 @@ final class TypeResolver {
     if (value is LocalDate) {
       return _builtin(LocalDate, TypeIds.date);
     }
+    if (value is Duration) {
+      return _builtin(Duration, TypeIds.duration);
+    }
     if (value is Timestamp) {
       return _builtin(Timestamp, TypeIds.timestamp);
     }
+    if (value is DateTime) {
+      return _builtin(DateTime, TypeIds.timestamp);
+    }
     throw StateError(
       'Type $runtimeType is not registered. Register generated types with '
       'their generated library namespace, or register a serializer explicitly.',
@@ -410,6 +436,7 @@ final class TypeResolver {
       case TypeIds.uint64:
       case TypeIds.varUint64:
       case TypeIds.taggedUint64:
+      case TypeIds.bfloat16:
       case TypeIds.float16:
       case TypeIds.float32:
       case TypeIds.float64:
@@ -417,7 +444,9 @@ final class TypeResolver {
       case TypeIds.list:
       case TypeIds.set:
       case TypeIds.map:
+      case TypeIds.none:
       case TypeIds.binary:
+      case TypeIds.duration:
       case TypeIds.timestamp:
       case TypeIds.date:
       case TypeIds.boolArray:
@@ -430,6 +459,7 @@ final class TypeResolver {
       case TypeIds.uint32Array:
       case TypeIds.uint64Array:
       case TypeIds.float16Array:
+      case TypeIds.bfloat16Array:
       case TypeIds.float32Array:
       case TypeIds.float64Array:
         return _builtin(fieldType.type, fieldType.typeId);
@@ -804,6 +834,7 @@ final class TypeResolver {
         fieldType.typeId == TypeIds.string ||
         fieldType.typeId == TypeIds.binary ||
         fieldType.typeId == TypeIds.date ||
+        fieldType.typeId == TypeIds.duration ||
         fieldType.typeId == TypeIds.timestamp) {
       return fieldType.typeId;
     }
@@ -988,21 +1019,23 @@ final class TypeResolver {
       case TypeIds.taggedInt64:
         return _builtin(int, TypeIds.taggedInt64);
       case TypeIds.uint8:
-        return _builtin(UInt8, TypeIds.uint8);
+        return _builtin(Uint8, TypeIds.uint8);
       case TypeIds.uint16:
-        return _builtin(UInt16, TypeIds.uint16);
+        return _builtin(Uint16, TypeIds.uint16);
       case TypeIds.uint32:
-        return _builtin(UInt32, TypeIds.uint32);
+        return _builtin(Uint32, TypeIds.uint32);
       case TypeIds.varUint32:
-        return _builtin(UInt32, TypeIds.varUint32);
+        return _builtin(Uint32, TypeIds.varUint32);
       case TypeIds.uint64:
-        return _builtin(int, TypeIds.uint64);
+        return _builtin(Uint64, TypeIds.uint64);
       case TypeIds.varUint64:
-        return _builtin(int, TypeIds.varUint64);
+        return _builtin(Uint64, TypeIds.varUint64);
       case TypeIds.taggedUint64:
-        return _builtin(int, TypeIds.taggedUint64);
+        return _builtin(Uint64, TypeIds.taggedUint64);
       case TypeIds.float16:
         return _builtin(Float16, TypeIds.float16);
+      case TypeIds.bfloat16:
+        return _builtin(Bfloat16, TypeIds.bfloat16);
       case TypeIds.float32:
         return _builtin(Float32, TypeIds.float32);
       case TypeIds.float64:
@@ -1015,12 +1048,16 @@ final class TypeResolver {
         return _builtin(Set, TypeIds.set);
       case TypeIds.map:
         return _builtin(Map, TypeIds.map);
+      case TypeIds.none:
+        return _builtin(Null, TypeIds.none);
       case TypeIds.binary:
         return _builtin(Uint8List, TypeIds.binary);
       case TypeIds.date:
         return _builtin(LocalDate, TypeIds.date);
+      case TypeIds.duration:
+        return _builtin(Duration, TypeIds.duration);
       case TypeIds.timestamp:
-        return _builtin(Timestamp, TypeIds.timestamp);
+        return _builtin(DateTime, TypeIds.timestamp);
       case TypeIds.boolArray:
         return _builtin(List<bool>, TypeIds.boolArray);
       case TypeIds.int8Array:
@@ -1040,7 +1077,9 @@ final class TypeResolver {
       case TypeIds.uint64Array:
         return _builtin(Uint64List, TypeIds.uint64Array);
       case TypeIds.float16Array:
-        return _builtin(Uint16List, TypeIds.float16Array);
+        return _builtin(Float16List, TypeIds.float16Array);
+      case TypeIds.bfloat16Array:
+        return _builtin(Bfloat16List, TypeIds.bfloat16Array);
       case TypeIds.float32Array:
         return _builtin(Float32List, TypeIds.float32Array);
       case TypeIds.float64Array:
@@ -1051,7 +1090,8 @@ final class TypeResolver {
   }
 
   TypeInfo _builtin(Type type, int typeId) {
-    final cached = _builtinByTypeId[typeId];
+    final key = _BuiltinKey(type, typeId);
+    final cached = _builtinByKey[key];
     if (cached != null) {
       return cached;
     }
@@ -1060,7 +1100,7 @@ final class TypeResolver {
       kind: RegistrationKind.builtin,
       typeId: typeId,
       supportsRef: TypeIds.supportsRef(typeId),
-      serializer: _builtinSerializerFor(typeId),
+      serializer: _builtinSerializerFor(typeId, type),
       structSerializer: null,
       userTypeId: null,
       namespace: null,
@@ -1070,11 +1110,11 @@ final class TypeResolver {
       typeDef: null,
       remoteTypeDef: null,
     );
-    _builtinByTypeId[typeId] = resolved;
+    _builtinByKey[key] = resolved;
     return resolved;
   }
 
-  Serializer<Object?> _builtinSerializerFor(int typeId) {
+  Serializer<Object?> _builtinSerializerFor(int typeId, Type type) {
     switch (typeId) {
       case TypeIds.boolType:
         return boolSerializer as Serializer<Object?>;
@@ -1108,12 +1148,16 @@ final class TypeResolver {
         return taggedUint64Serializer as Serializer<Object?>;
       case TypeIds.float16:
         return float16Serializer as Serializer<Object?>;
+      case TypeIds.bfloat16:
+        return bfloat16Serializer as Serializer<Object?>;
       case TypeIds.float32:
         return float32Serializer as Serializer<Object?>;
       case TypeIds.float64:
         return float64Serializer as Serializer<Object?>;
       case TypeIds.string:
         return stringSerializer as Serializer<Object?>;
+      case TypeIds.none:
+        return noneSerializer as Serializer<Object?>;
       case TypeIds.binary:
       case TypeIds.uint8Array:
         return binarySerializer as Serializer<Object?>;
@@ -1133,6 +1177,10 @@ final class TypeResolver {
         return uint32ArraySerializer as Serializer<Object?>;
       case TypeIds.uint64Array:
         return uint64ArraySerializer as Serializer<Object?>;
+      case TypeIds.float16Array:
+        return float16ArraySerializer as Serializer<Object?>;
+      case TypeIds.bfloat16Array:
+        return bfloat16ArraySerializer as Serializer<Object?>;
       case TypeIds.float32Array:
         return float32ArraySerializer as Serializer<Object?>;
       case TypeIds.float64Array:
@@ -1145,8 +1193,13 @@ final class TypeResolver {
         return mapSerializer as Serializer<Object?>;
       case TypeIds.date:
         return localDateSerializer as Serializer<Object?>;
+      case TypeIds.duration:
+        return durationSerializer as Serializer<Object?>;
       case TypeIds.timestamp:
-        return timestampSerializer as Serializer<Object?>;
+        if (type == Timestamp) {
+          return timestampSerializer as Serializer<Object?>;
+        }
+        return dateTimeSerializer as Serializer<Object?>;
       default:
         throw StateError('Unsupported builtin type id $typeId.');
     }
@@ -1178,30 +1231,45 @@ final class TypeResolver {
     if (type == int) {
       return TypeIds.varInt64;
     }
-    if (type == UInt8) {
+    if (type == Uint8) {
       return TypeIds.uint8;
     }
-    if (type == UInt16) {
+    if (type == Uint16) {
       return TypeIds.uint16;
     }
-    if (type == UInt32) {
+    if (type == Uint32) {
       return TypeIds.uint32;
     }
+    if (type == Uint64) {
+      return TypeIds.uint64;
+    }
     if (type == Uint64List) {
       return TypeIds.uint64Array;
     }
     if (type == Float16) {
       return TypeIds.float16;
     }
+    if (type == Bfloat16) {
+      return TypeIds.bfloat16;
+    }
     if (type == Float32) {
       return TypeIds.float32;
     }
+    if (type == Float16List) {
+      return TypeIds.float16Array;
+    }
+    if (type == Bfloat16List) {
+      return TypeIds.bfloat16Array;
+    }
     if (type == double) {
       return TypeIds.float64;
     }
     if (type == String) {
       return TypeIds.string;
     }
+    if (type == Null) {
+      return TypeIds.none;
+    }
     if (type == List) {
       return TypeIds.list;
     }
@@ -1214,9 +1282,15 @@ final class TypeResolver {
     if (type == Uint8List) {
       return TypeIds.binary;
     }
+    if (type == Duration) {
+      return TypeIds.duration;
+    }
     if (type == Timestamp) {
       return TypeIds.timestamp;
     }
+    if (type == DateTime) {
+      return TypeIds.timestamp;
+    }
     if (type == LocalDate) {
       return TypeIds.date;
     }
@@ -1354,6 +1428,21 @@ final class TypeResolver {
   }
 }
 
+final class _BuiltinKey {
+  final Type type;
+  final int typeId;
+
+  const _BuiltinKey(this.type, this.typeId);
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      other is _BuiltinKey && other.type == type && other.typeId == typeId;
+
+  @override
+  int get hashCode => Object.hash(type, typeId);
+}
+
 final class _NamedTypeReadCacheEntry {
   final int wireTypeId;
   final EncodedMetaString namespace;
diff --git a/dart/packages/fory/lib/src/serializer/collection_serializers.dart b/dart/packages/fory/lib/src/serializer/collection_serializers.dart
index 1ab08613a..828a6ac77 100644
--- a/dart/packages/fory/lib/src/serializer/collection_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/collection_serializers.dart
@@ -26,6 +26,7 @@ import 'package:fory/src/resolver/type_resolver.dart';
 import 'package:fory/src/serializer/primitive_serializers.dart';
 import 'package:fory/src/serializer/scalar_serializers.dart';
 import 'package:fory/src/serializer/serializer.dart';
+import 'package:fory/src/serializer/serializer_support.dart';
 
 @pragma('vm:prefer-inline')
 void _writeDirectTypeInfoValue(
@@ -192,7 +193,10 @@ T readFieldTypeValue<T>(
     return context.readRef() as T;
   }
   if (fieldType.isPrimitive && !fieldType.nullable) {
-    return context.readPrimitiveValue(fieldType.typeId) as T;
+    return convertPrimitiveFieldValue(
+      context.readPrimitiveValue(fieldType.typeId),
+      fieldType,
+    ) as T;
   }
   if (!usesDeclaredType) {
     if (fieldType.ref) {
@@ -432,10 +436,14 @@ final class ListSerializer extends Serializer<List> {
 
   static List<Object?> readPayload(
     ReadContext context,
-    FieldType? elementFieldType,
-  ) {
+    FieldType? elementFieldType, {
+    bool hasPreservedRef = false,
+  }) {
     final state = _prepareListRead(context, elementFieldType);
     final result = List<Object?>.filled(state.size, null, growable: false);
+    if (hasPreservedRef) {
+      context.reference(result);
+    }
     if (state.size == 0) {
       return result;
     }
@@ -472,10 +480,16 @@ final class SetSerializer extends Serializer<Set> {
 
   static Set<Object?> readPayload(
     ReadContext context,
-    FieldType? elementFieldType,
-  ) {
+    FieldType? elementFieldType, {
+    bool hasPreservedRef = false,
+  }) {
     return Set<Object?>.of(
-        ListSerializer.readPayload(context, elementFieldType));
+      ListSerializer.readPayload(
+        context,
+        elementFieldType,
+        hasPreservedRef: hasPreservedRef,
+      ),
+    );
   }
 }
 
diff --git a/dart/packages/fory/lib/src/serializer/map_serializers.dart b/dart/packages/fory/lib/src/serializer/map_serializers.dart
index 82659fca6..784924b89 100644
--- a/dart/packages/fory/lib/src/serializer/map_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/map_serializers.dart
@@ -229,14 +229,16 @@ final class MapSerializer extends Serializer<Map> {
   static Map<Object?, Object?> readPayload(
     ReadContext context,
     FieldType? keyFieldType,
-    FieldType? valueFieldType,
-  ) {
+    FieldType? valueFieldType, {
+    bool hasPreservedRef = false,
+  }) {
     return readTypedMapPayload<Object?, Object?>(
       context,
       keyFieldType,
       valueFieldType,
       (value) => value,
       (value) => value,
+      hasPreservedRef: hasPreservedRef,
     );
   }
 }
@@ -248,8 +250,9 @@ Map<K, V> readTypedMapPayload<K, V>(
   FieldType? keyFieldType,
   FieldType? valueFieldType,
   K Function(Object? value) convertKey,
-  V Function(Object? value) convertValue,
-) {
+  V Function(Object? value) convertValue, {
+  bool hasPreservedRef = false,
+}) {
   var remaining = context.buffer.readVarUint32();
   if (remaining > context.config.maxCollectionSize) {
     throw StateError(
@@ -264,6 +267,9 @@ Map<K, V> readTypedMapPayload<K, V>(
           ? null
           : context.typeResolver.resolveFieldType(valueFieldType);
   final result = <K, V>{};
+  if (hasPreservedRef) {
+    context.reference(result);
+  }
   while (remaining > 0) {
     final header = context.buffer.readUint8();
     final keyHasNull = (header & 0x02) != 0;
@@ -296,13 +302,13 @@ Map<K, V> readTypedMapPayload<K, V>(
     final valueTypeInfo = valueDeclared ? null : context.readTypeMetaValue();
     final tracksDepth =
         ((keyDeclared ? declaredKeyTypeInfo : keyTypeInfo) != null &&
-            tracksNestedPayloadDepth(
-              keyDeclared ? declaredKeyTypeInfo! : keyTypeInfo!,
-            )) ||
-        ((valueDeclared ? declaredValueTypeInfo : valueTypeInfo) != null &&
-            tracksNestedPayloadDepth(
-              valueDeclared ? declaredValueTypeInfo! : valueTypeInfo!,
-            ));
+                tracksNestedPayloadDepth(
+                  keyDeclared ? declaredKeyTypeInfo! : keyTypeInfo!,
+                )) ||
+            ((valueDeclared ? declaredValueTypeInfo : valueTypeInfo) != null &&
+                tracksNestedPayloadDepth(
+                  valueDeclared ? declaredValueTypeInfo! : valueTypeInfo!,
+                ));
     if (tracksDepth) {
       context.increaseDepth();
     }
diff --git a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart
index bb68f17e3..2b00b68e6 100644
--- a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart
@@ -21,9 +21,16 @@ import 'package:fory/src/context/read_context.dart';
 import 'package:fory/src/context/write_context.dart';
 import 'package:fory/src/meta/type_ids.dart';
 import 'package:fory/src/serializer/serializer.dart';
-import 'package:fory/src/types/fixed_ints.dart';
+import 'package:fory/src/types/bfloat16.dart';
 import 'package:fory/src/types/float16.dart';
 import 'package:fory/src/types/float32.dart';
+import 'package:fory/src/types/int16.dart';
+import 'package:fory/src/types/int32.dart';
+import 'package:fory/src/types/int8.dart';
+import 'package:fory/src/types/uint16.dart';
+import 'package:fory/src/types/uint32.dart';
+import 'package:fory/src/types/uint64.dart';
+import 'package:fory/src/types/uint8.dart';
 
 final class PrimitiveSerializer<T> extends Serializer<T> {
   final int typeId;
@@ -79,29 +86,32 @@ final class PrimitiveSerializer<T> extends Serializer<T> {
         buffer.writeTaggedInt64(value as int);
         return;
       case TypeIds.uint8:
-        buffer.writeUint8(value is UInt8 ? value.value : value as int);
+        buffer.writeUint8(value is Uint8 ? value.value : value as int);
         return;
       case TypeIds.uint16:
-        buffer.writeUint16(value is UInt16 ? value.value : value as int);
+        buffer.writeUint16(value is Uint16 ? value.value : value as int);
         return;
       case TypeIds.uint32:
-        buffer.writeUint32(value is UInt32 ? value.value : value as int);
+        buffer.writeUint32(value is Uint32 ? value.value : value as int);
         return;
       case TypeIds.varUint32:
-        buffer.writeVarUint32(value is UInt32 ? value.value : value as int);
+        buffer.writeVarUint32(value is Uint32 ? value.value : value as int);
         return;
       case TypeIds.uint64:
-        buffer.writeUint64(value as int);
+        buffer.writeUint64(value is Uint64 ? value.value : value as int);
         return;
       case TypeIds.varUint64:
-        buffer.writeVarUint64(value as int);
+        buffer.writeVarUint64(value is Uint64 ? value.value : value as int);
         return;
       case TypeIds.taggedUint64:
-        buffer.writeTaggedUint64(value as int);
+        buffer.writeTaggedUint64(value is Uint64 ? value.value : value as int);
         return;
       case TypeIds.float16:
         buffer.writeFloat16(value as Float16);
         return;
+      case TypeIds.bfloat16:
+        buffer.writeBfloat16(value as Bfloat16);
+        return;
       case TypeIds.float32:
         buffer.writeFloat32((value as Float32).value);
         return;
@@ -136,21 +146,23 @@ final class PrimitiveSerializer<T> extends Serializer<T> {
       case TypeIds.taggedInt64:
         return buffer.readTaggedInt64();
       case TypeIds.uint8:
-        return UInt8(buffer.readUint8());
+        return Uint8(buffer.readUint8());
       case TypeIds.uint16:
-        return UInt16(buffer.readUint16());
+        return Uint16(buffer.readUint16());
       case TypeIds.uint32:
-        return UInt32(buffer.readUint32());
+        return Uint32(buffer.readUint32());
       case TypeIds.varUint32:
-        return UInt32(buffer.readVarUint32());
+        return Uint32(buffer.readVarUint32());
       case TypeIds.uint64:
-        return buffer.readUint64();
+        return Uint64(buffer.readUint64());
       case TypeIds.varUint64:
-        return buffer.readVarUint64();
+        return Uint64(buffer.readVarUint64());
       case TypeIds.taggedUint64:
-        return buffer.readTaggedUint64();
+        return Uint64(buffer.readTaggedUint64());
       case TypeIds.float16:
         return buffer.readFloat16();
+      case TypeIds.bfloat16:
+        return buffer.readBfloat16();
       case TypeIds.float32:
         return Float32(buffer.readFloat32());
       case TypeIds.float64:
@@ -190,40 +202,41 @@ const PrimitiveSerializer<int> varInt64Serializer = PrimitiveSerializer<int>(
   TypeIds.varInt64,
   supportsRef: false,
 );
-const PrimitiveSerializer<int> taggedInt64Serializer =
-    PrimitiveSerializer<int>(
+const PrimitiveSerializer<int> taggedInt64Serializer = PrimitiveSerializer<int>(
   TypeIds.taggedInt64,
   supportsRef: false,
 );
-const PrimitiveSerializer<UInt8> uint8Serializer = PrimitiveSerializer<UInt8>(
+const PrimitiveSerializer<Uint8> uint8Serializer = PrimitiveSerializer<Uint8>(
   TypeIds.uint8,
   supportsRef: false,
 );
-const PrimitiveSerializer<UInt16> uint16Serializer =
-    PrimitiveSerializer<UInt16>(
+const PrimitiveSerializer<Uint16> uint16Serializer =
+    PrimitiveSerializer<Uint16>(
   TypeIds.uint16,
   supportsRef: false,
 );
-const PrimitiveSerializer<UInt32> uint32Serializer =
-    PrimitiveSerializer<UInt32>(
+const PrimitiveSerializer<Uint32> uint32Serializer =
+    PrimitiveSerializer<Uint32>(
   TypeIds.uint32,
   supportsRef: false,
 );
-const PrimitiveSerializer<UInt32> varUint32Serializer =
-    PrimitiveSerializer<UInt32>(
+const PrimitiveSerializer<Uint32> varUint32Serializer =
+    PrimitiveSerializer<Uint32>(
   TypeIds.varUint32,
   supportsRef: false,
 );
-const PrimitiveSerializer<int> uint64Serializer = PrimitiveSerializer<int>(
+const PrimitiveSerializer<Uint64> uint64Serializer =
+    PrimitiveSerializer<Uint64>(
   TypeIds.uint64,
   supportsRef: false,
 );
-const PrimitiveSerializer<int> varUint64Serializer = PrimitiveSerializer<int>(
+const PrimitiveSerializer<Uint64> varUint64Serializer =
+    PrimitiveSerializer<Uint64>(
   TypeIds.varUint64,
   supportsRef: false,
 );
-const PrimitiveSerializer<int> taggedUint64Serializer =
-    PrimitiveSerializer<int>(
+const PrimitiveSerializer<Uint64> taggedUint64Serializer =
+    PrimitiveSerializer<Uint64>(
   TypeIds.taggedUint64,
   supportsRef: false,
 );
@@ -232,6 +245,11 @@ const PrimitiveSerializer<Float16> float16Serializer =
   TypeIds.float16,
   supportsRef: false,
 );
+const PrimitiveSerializer<Bfloat16> bfloat16Serializer =
+    PrimitiveSerializer<Bfloat16>(
+  TypeIds.bfloat16,
+  supportsRef: false,
+);
 const PrimitiveSerializer<Float32> float32Serializer =
     PrimitiveSerializer<Float32>(
   TypeIds.float32,
diff --git a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart
index c26f7c9b5..3fe0127f5 100644
--- a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart
@@ -23,8 +23,21 @@ import 'package:fory/src/context/read_context.dart';
 import 'package:fory/src/context/write_context.dart';
 import 'package:fory/src/serializer/serializer.dart';
 import 'package:fory/src/string_encoding.dart';
-import 'package:fory/src/types/local_date.dart';
-import 'package:fory/src/types/timestamp.dart';
+
+final class NoneSerializer extends Serializer<Null> {
+  const NoneSerializer();
+
+  @override
+  bool get supportsRef => false;
+
+  @override
+  void write(WriteContext context, Null value) {}
+
+  @override
+  Null read(ReadContext context) {
+    return null;
+  }
+}
 
 final class StringSerializer extends Serializer<String> {
   const StringSerializer();
@@ -91,45 +104,6 @@ final class BinarySerializer extends Serializer<Uint8List> {
   }
 }
 
-final class LocalDateSerializer extends Serializer<LocalDate> {
-  const LocalDateSerializer();
-
-  @override
-  bool get supportsRef => false;
-
-  @override
-  void write(WriteContext context, LocalDate value) {
-    context.buffer.writeInt32(value.toEpochDay());
-  }
-
-  @override
-  LocalDate read(ReadContext context) {
-    return LocalDate.fromEpochDay(context.buffer.readInt32());
-  }
-}
-
-final class TimestampSerializer extends Serializer<Timestamp> {
-  const TimestampSerializer();
-
-  @override
-  bool get supportsRef => false;
-
-  @override
-  void write(WriteContext context, Timestamp value) {
-    context.buffer.writeInt64(value.seconds);
-    context.buffer.writeUint32(value.nanoseconds);
-  }
-
-  @override
-  Timestamp read(ReadContext context) {
-    return Timestamp(
-      context.buffer.readInt64(),
-      context.buffer.readUint32(),
-    );
-  }
-}
-
+const NoneSerializer noneSerializer = NoneSerializer();
 const StringSerializer stringSerializer = StringSerializer();
 const BinarySerializer binarySerializer = BinarySerializer();
-const LocalDateSerializer localDateSerializer = LocalDateSerializer();
-const TimestampSerializer timestampSerializer = TimestampSerializer();
diff --git a/dart/packages/fory/lib/src/serializer/serializer_support.dart b/dart/packages/fory/lib/src/serializer/serializer_support.dart
index f11ae8af7..3ca7228bc 100644
--- a/dart/packages/fory/lib/src/serializer/serializer_support.dart
+++ b/dart/packages/fory/lib/src/serializer/serializer_support.dart
@@ -22,8 +22,17 @@ import 'package:fory/src/context/ref_writer.dart';
 import 'package:fory/src/context/write_context.dart';
 import 'package:fory/src/meta/field_info.dart';
 import 'package:fory/src/meta/field_type.dart';
+import 'package:fory/src/meta/type_ids.dart';
 import 'package:fory/src/resolver/type_resolver.dart';
 import 'package:fory/src/serializer/serialization_field_info.dart';
+import 'package:fory/src/types/float32.dart';
+import 'package:fory/src/types/int16.dart';
+import 'package:fory/src/types/int32.dart';
+import 'package:fory/src/types/int8.dart';
+import 'package:fory/src/types/uint16.dart';
+import 'package:fory/src/types/uint32.dart';
+import 'package:fory/src/types/uint64.dart';
+import 'package:fory/src/types/uint8.dart';
 
 final class DeferredReadRef {
   final int id;
@@ -45,6 +54,37 @@ bool fieldUsesDeclaredType(
   return field.usesDeclaredType(resolver);
 }
 
+Object convertPrimitiveFieldValue(Object value, FieldType fieldType) {
+  if (fieldType.type == int) {
+    switch (fieldType.typeId) {
+      case TypeIds.int8:
+        return (value as Int8).value;
+      case TypeIds.int16:
+        return (value as Int16).value;
+      case TypeIds.int32:
+      case TypeIds.varInt32:
+        return (value as Int32).value;
+      case TypeIds.uint8:
+        return (value as Uint8).value;
+      case TypeIds.uint16:
+        return (value as Uint16).value;
+      case TypeIds.uint32:
+      case TypeIds.varUint32:
+        return (value as Uint32).value;
+      case TypeIds.uint64:
+      case TypeIds.varUint64:
+      case TypeIds.taggedUint64:
+        return (value as Uint64).value;
+      default:
+        return value;
+    }
+  }
+  if (fieldType.type == double && fieldType.typeId == TypeIds.float32) {
+    return (value as Float32).value;
+  }
+  return value;
+}
+
 void writeFieldValue(
   WriteContext context,
   SerializationFieldInfo field,
@@ -115,7 +155,10 @@ T readFieldValue<T>(
     return context.readRef() as T;
   }
   if (fieldType.isPrimitive && !fieldType.nullable) {
-    return context.readPrimitiveValue(fieldType.typeId) as T;
+    return convertPrimitiveFieldValue(
+      context.readPrimitiveValue(fieldType.typeId),
+      fieldType,
+    ) as T;
   }
   final declaredTypeInfo = fieldDeclaredTypeInfo(context.typeResolver, field);
   final usesDeclaredType = fieldUsesDeclaredType(context.typeResolver, field);
@@ -162,7 +205,10 @@ Object? readCompatibleField(
     return context.readRef();
   }
   if (fieldType.isPrimitive && !fieldType.nullable) {
-    return context.readPrimitiveValue(fieldType.typeId);
+    return convertPrimitiveFieldValue(
+      context.readPrimitiveValue(fieldType.typeId),
+      fieldType,
+    );
   }
   final declaredTypeInfo = _compatibleFieldDeclaredTypeInfo(
     context.typeResolver,
diff --git a/dart/packages/fory/lib/src/serializer/struct_serializer.dart b/dart/packages/fory/lib/src/serializer/struct_serializer.dart
index f6651c556..f0b4df1c3 100644
--- a/dart/packages/fory/lib/src/serializer/struct_serializer.dart
+++ b/dart/packages/fory/lib/src/serializer/struct_serializer.dart
@@ -55,8 +55,6 @@ final class StructSerializer extends Serializer<Object?> {
       <String, SerializationFieldInfo>{
     for (final field in _localFields) field.identifier: field,
   };
-  final Expando<_CompatibleWriteLayout> _compatibleWriteLayouts =
-      Expando<_CompatibleWriteLayout>('fory_compatible_write_layout');
   final Expando<_CompatibleReadLayout> _compatibleReadLayouts =
       Expando<_CompatibleReadLayout>('fory_compatible_read_layout');
   final GeneratedStructCompatibleFactory<Object>? _compatibleFactory;
@@ -92,11 +90,7 @@ final class StructSerializer extends Serializer<Object?> {
     TypeInfo resolved,
     Object value,
   ) {
-    final layout = _compatibleWriteLayoutForValue(context, resolved, value);
-    if (layout == null) {
-      return resolved.typeDef!;
-    }
-    return layout.typeDef;
+    return resolved.typeDef!;
   }
 
   void writeValue(
@@ -144,7 +138,7 @@ final class StructSerializer extends Serializer<Object?> {
       _payloadSerializer,
       resolved,
       hasCurrentPreservedRef: hasCurrentPreservedRef,
-    );
+    ) as Object;
     internal.structReadSlots = previousReadSlots;
     _rememberRemoteMetadata(internal, resolved, value);
     return value;
@@ -155,64 +149,11 @@ final class StructSerializer extends Serializer<Object?> {
     TypeInfo resolved,
     Object value,
   ) {
-    final layout = _compatibleWriteLayoutForValue(context, resolved, value);
     final previous = context.structWriteSlots;
-    context.structWriteSlots = layout == null
-        ? null
-        : StructWriteSlots(layout.fields, _localFields.length);
+    context.structWriteSlots = null;
     return previous;
   }
 
-  _CompatibleWriteLayout? _compatibleWriteLayoutForValue(
-    WriteContext context,
-    TypeInfo resolved,
-    Object value,
-  ) {
-    if (!_typeDef.evolving || !context.config.compatible) {
-      return null;
-    }
-    final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value);
-    if (remoteTypeDef == null) {
-      return null;
-    }
-    final cached = _compatibleWriteLayouts[remoteTypeDef];
-    if (cached != null) {
-      return cached;
-    }
-    final orderedFields = <SerializationFieldInfo>[];
-    final usedIdentifiers = <String>{};
-    for (final remoteField in remoteTypeDef.fields) {
-      final localField = _localFieldsByIdentifier[remoteField.identifier];
-      if (localField == null) {
-        continue;
-      }
-      final mergedField = _typeResolver.serializationFieldInfo(
-        mergeCompatibleWriteField(localField.field, remoteField),
-        slot: localField.slot,
-      );
-      orderedFields.add(mergedField);
-      usedIdentifiers.add(remoteField.identifier);
-    }
-    for (final localField in _localFields) {
-      if (usedIdentifiers.add(localField.identifier)) {
-        orderedFields.add(localField);
-      }
-    }
-    final fields = List<SerializationFieldInfo>.unmodifiable(orderedFields);
-    final typeDef = context.typeResolver.typeDefForResolved(
-      resolved,
-      fields: List<FieldInfo>.unmodifiable(
-        List<FieldInfo>.generate(
-          fields.length,
-          (index) => fields[index].field,
-        ),
-      ),
-    );
-    final layout = _CompatibleWriteLayout(fields, typeDef);
-    _compatibleWriteLayouts[remoteTypeDef] = layout;
-    return layout;
-  }
-
   Object _readCompatible(
     ReadContext context,
     TypeInfo resolved, {
@@ -272,7 +213,7 @@ final class StructSerializer extends Serializer<Object?> {
       _payloadSerializer,
       resolved,
       hasCurrentPreservedRef: hasCurrentPreservedRef,
-    );
+    ) as Object;
     context.structReadSlots = previousCompatibleFields;
     _rememberRemoteMetadata(context, resolved, value);
     return value;
@@ -322,13 +263,6 @@ final class StructSerializer extends Serializer<Object?> {
   }
 }
 
-final class _CompatibleWriteLayout {
-  final List<SerializationFieldInfo> fields;
-  final TypeDef typeDef;
-
-  const _CompatibleWriteLayout(this.fields, this.typeDef);
-}
-
 final class _CompatibleReadLayout {
   final List<FieldInfo> remoteFields;
   final List<SerializationFieldInfo?> fields;
diff --git a/dart/packages/fory/lib/src/serializer/time_serializers.dart b/dart/packages/fory/lib/src/serializer/time_serializers.dart
new file mode 100644
index 000000000..6ce2c8a9b
--- /dev/null
+++ b/dart/packages/fory/lib/src/serializer/time_serializers.dart
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+import 'package:fory/src/context/read_context.dart';
+import 'package:fory/src/context/write_context.dart';
+import 'package:fory/src/serializer/serializer.dart';
+import 'package:fory/src/types/local_date.dart';
+import 'package:fory/src/types/timestamp.dart';
+
+const int _nanosecondsPerSecond = 1000000000;
+final Expando<_ExactDurationWire> _exactDurationWire =
+    Expando<_ExactDurationWire>('fory_exact_duration_wire');
+
+final class _ExactDurationWire {
+  final int seconds;
+  final int nanoseconds;
+  final int microseconds;
+
+  const _ExactDurationWire(this.seconds, this.nanoseconds, this.microseconds);
+}
+
+void _validateDurationNanoseconds(int nanoseconds) {
+  if (nanoseconds < -999999999 || nanoseconds > 999999999) {
+    throw StateError(
+      'Duration nanoseconds $nanoseconds are out of range [-999999999, 999999999].',
+    );
+  }
+}
+
+void _validateTimestampNanoseconds(int nanoseconds) {
+  if (nanoseconds < 0 || nanoseconds >= _nanosecondsPerSecond) {
+    throw StateError(
+      'Timestamp nanoseconds $nanoseconds are out of range [0, 999999999].',
+    );
+  }
+}
+
+void _validateDateTimeNanoseconds(int nanoseconds) {
+  _validateTimestampNanoseconds(nanoseconds);
+  if (nanoseconds.remainder(1000) != 0) {
+    throw StateError(
+      'Timestamp nanoseconds $nanoseconds exceed Dart DateTime microsecond precision.',
+    );
+  }
+}
+
+int durationWireSeconds(Duration value) {
+  final exact = _exactDurationWire[value];
+  if (exact != null && exact.microseconds == value.inMicroseconds) {
+    return exact.seconds;
+  }
+  return value.inMicroseconds ~/ Duration.microsecondsPerSecond;
+}
+
+int durationWireNanoseconds(Duration value) {
+  final exact = _exactDurationWire[value];
+  if (exact != null && exact.microseconds == value.inMicroseconds) {
+    return exact.nanoseconds;
+  }
+  return value.inMicroseconds.remainder(Duration.microsecondsPerSecond) * 1000;
+}
+
+Duration durationFromWire(int seconds, int nanoseconds) {
+  _validateDurationNanoseconds(nanoseconds);
+  if ((seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0)) {
+    throw StateError(
+      'Duration wire value has inconsistent signs: seconds=$seconds, nanoseconds=$nanoseconds.',
+    );
+  }
+  final totalNanoseconds = seconds * _nanosecondsPerSecond + nanoseconds;
+  final microseconds = totalNanoseconds ~/ 1000;
+  final value = Duration(microseconds: microseconds);
+  if (nanoseconds.remainder(1000) != 0) {
+    _exactDurationWire[value] = _ExactDurationWire(
+      seconds,
+      nanoseconds,
+      microseconds,
+    );
+  }
+  return value;
+}
+
+int timestampWireNanoseconds(Timestamp value) {
+  _validateTimestampNanoseconds(value.nanoseconds);
+  return value.nanoseconds;
+}
+
+int dateTimeWireSeconds(DateTime value) {
+  final utcValue = value.toUtc();
+  final microseconds = utcValue.microsecondsSinceEpoch;
+  var seconds = microseconds ~/ Duration.microsecondsPerSecond;
+  var micros = microseconds.remainder(Duration.microsecondsPerSecond);
+  if (micros < 0) {
+    micros += Duration.microsecondsPerSecond;
+    seconds -= 1;
+  }
+  return seconds;
+}
+
+int dateTimeWireNanoseconds(DateTime value) {
+  final utcValue = value.toUtc();
+  var micros =
+      utcValue.microsecondsSinceEpoch.remainder(Duration.microsecondsPerSecond);
+  if (micros < 0) {
+    micros += Duration.microsecondsPerSecond;
+  }
+  return micros * 1000;
+}
+
+Timestamp timestampFromWire(int seconds, int nanoseconds) {
+  _validateTimestampNanoseconds(nanoseconds);
+  return Timestamp(seconds, nanoseconds);
+}
+
+DateTime dateTimeFromWire(int seconds, int nanoseconds) {
+  _validateDateTimeNanoseconds(nanoseconds);
+  return DateTime.fromMicrosecondsSinceEpoch(
+    seconds * Duration.microsecondsPerSecond + nanoseconds ~/ 1000,
+    isUtc: true,
+  );
+}
+
+final class LocalDateSerializer extends Serializer<LocalDate> {
+  const LocalDateSerializer();
+
+  @override
+  bool get supportsRef => false;
+
+  @override
+  void write(WriteContext context, LocalDate value) {
+    context.buffer.writeInt32(value.toEpochDay());
+  }
+
+  @override
+  LocalDate read(ReadContext context) {
+    return LocalDate.fromEpochDay(context.buffer.readInt32());
+  }
+}
+
+final class DurationSerializer extends Serializer<Duration> {
+  const DurationSerializer();
+
+  @override
+  bool get supportsRef => false;
+
+  @override
+  void write(WriteContext context, Duration value) {
+    context.buffer.writeVarInt64(durationWireSeconds(value));
+    context.buffer.writeInt32(durationWireNanoseconds(value));
+  }
+
+  @override
+  Duration read(ReadContext context) {
+    return durationFromWire(
+      context.buffer.readVarInt64(),
+      context.buffer.readInt32(),
+    );
+  }
+}
+
+final class TimestampSerializer extends Serializer<Timestamp> {
+  const TimestampSerializer();
+
+  @override
+  bool get supportsRef => false;
+
+  @override
+  void write(WriteContext context, Timestamp value) {
+    context.buffer.writeInt64(value.seconds);
+    context.buffer.writeUint32(timestampWireNanoseconds(value));
+  }
+
+  @override
+  Timestamp read(ReadContext context) {
+    return timestampFromWire(
+        context.buffer.readInt64(), context.buffer.readUint32());
+  }
+}
+
+final class DateTimeSerializer extends Serializer<DateTime> {
+  const DateTimeSerializer();
+
+  @override
+  bool get supportsRef => false;
+
+  @override
+  void write(WriteContext context, DateTime value) {
+    context.buffer.writeInt64(dateTimeWireSeconds(value));
+    context.buffer.writeUint32(dateTimeWireNanoseconds(value));
+  }
+
+  @override
+  DateTime read(ReadContext context) {
+    return dateTimeFromWire(
+      context.buffer.readInt64(),
+      context.buffer.readUint32(),
+    );
+  }
+}
+
+const LocalDateSerializer localDateSerializer = LocalDateSerializer();
+const DurationSerializer durationSerializer = DurationSerializer();
+const TimestampSerializer timestampSerializer = TimestampSerializer();
+const DateTimeSerializer dateTimeSerializer = DateTimeSerializer();
diff --git a/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart b/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart
index ca8ca3dc6..c439a8636 100644
--- a/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart
@@ -23,15 +23,34 @@ import 'package:fory/src/context/read_context.dart';
 import 'package:fory/src/context/write_context.dart';
 import 'package:fory/src/meta/type_ids.dart';
 import 'package:fory/src/serializer/serializer.dart';
+import 'package:fory/src/types/bfloat16.dart';
+import 'package:fory/src/types/float16.dart';
 
 void writeTypedArrayBytes(
   WriteContext context,
-  TypedData values,
+  Object values,
 ) {
-  context.buffer.writeVarUint32(values.lengthInBytes);
-  context.buffer.writeBytes(
-    values.buffer.asUint8List(values.offsetInBytes, values.lengthInBytes),
-  );
+  final bytes = switch (values) {
+    TypedData typed => typed.buffer.asUint8List(
+        typed.offsetInBytes,
+        typed.lengthInBytes,
+      ),
+    Float16List typed => typed.buffer.asUint8List(
+        typed.offsetInBytes,
+        typed.lengthInBytes,
+      ),
+    Bfloat16List typed => typed.buffer.asUint8List(
+        typed.offsetInBytes,
+        typed.lengthInBytes,
+      ),
+    _ => throw ArgumentError.value(
+        values,
+        'values',
+        'Expected a supported contiguous typed array value.',
+      ),
+  };
+  context.buffer.writeVarUint32(bytes.length);
+  context.buffer.writeBytes(bytes);
 }
 
 T readTypedArrayBytes<T>(
@@ -77,7 +96,7 @@ final class BoolArraySerializer extends Serializer<List<bool>> {
   }
 }
 
-final class TypedArraySerializer<T extends TypedData> extends Serializer<T> {
+final class TypedArraySerializer<T> extends Serializer<T> {
   final int typeId;
   final int elementSize;
   final T Function(Uint8List bytes) viewBuilder;
@@ -99,7 +118,7 @@ final class TypedArraySerializer<T extends TypedData> extends Serializer<T> {
       context.buffer.writeBytes(bytes);
       return;
     }
-    writeTypedArrayBytes(context, value);
+    writeTypedArrayBytes(context, value as Object);
   }
 
   @override
@@ -155,6 +174,18 @@ const TypedArraySerializer<Uint64List> uint64ArraySerializer =
   8,
   _asUint64List,
 );
+const TypedArraySerializer<Float16List> float16ArraySerializer =
+    TypedArraySerializer<Float16List>(
+  TypeIds.float16Array,
+  2,
+  _asFloat16List,
+);
+const TypedArraySerializer<Bfloat16List> bfloat16ArraySerializer =
+    TypedArraySerializer<Bfloat16List>(
+  TypeIds.bfloat16Array,
+  2,
+  _asBfloat16List,
+);
 const TypedArraySerializer<Float32List> float32ArraySerializer =
     TypedArraySerializer<Float32List>(
   TypeIds.float32Array,
@@ -188,6 +219,15 @@ Uint16List _asUint16List(Uint8List bytes) => bytes.buffer.asUint16List(
       bytes.lengthInBytes ~/ 2,
     );
 
+Float16List _asFloat16List(Uint8List bytes) => Float16List.view(
+    bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 2);
+
+Bfloat16List _asBfloat16List(Uint8List bytes) => Bfloat16List.view(
+      bytes.buffer,
+      bytes.offsetInBytes,
+      bytes.lengthInBytes ~/ 2,
+    );
+
 Uint32List _asUint32List(Uint8List bytes) => bytes.buffer.asUint32List(
       bytes.offsetInBytes,
       bytes.lengthInBytes ~/ 4,
diff --git a/dart/packages/fory/lib/src/serializer/union_serializer.dart b/dart/packages/fory/lib/src/serializer/union_serializer.dart
index f42806fe0..52c3fba61 100644
--- a/dart/packages/fory/lib/src/serializer/union_serializer.dart
+++ b/dart/packages/fory/lib/src/serializer/union_serializer.dart
@@ -17,9 +17,42 @@
  * under the License.
  */
 
+import 'package:fory/src/context/read_context.dart';
+import 'package:fory/src/context/write_context.dart';
 import 'package:fory/src/serializer/serializer.dart';
+import 'package:meta/meta.dart';
 
 /// Union-specific serializer base used by manual union serializers.
+///
+/// The runtime owns the xlang wire contract:
+/// `case_id (varuint32) | case_value (Any-style payload)`.
 abstract class UnionSerializer<T> extends Serializer<T> {
   const UnionSerializer();
+
+  /// Returns the active union case identifier for [value].
+  int caseId(T value);
+
+  /// Returns the active case payload for [value].
+  Object? caseValue(T value);
+
+  /// Rebuilds a union value from a decoded [caseId] and [value].
+  T buildValue(int caseId, Object? value);
+
+  @nonVirtual
+  @override
+  void write(WriteContext context, T value) {
+    final activeCaseId = caseId(value);
+    if (activeCaseId < 0) {
+      throw StateError('Union case id must be non-negative: $activeCaseId.');
+    }
+    context.buffer.writeVarUint32(activeCaseId);
+    context.writeRef(caseValue(value));
+  }
+
+  @nonVirtual
+  @override
+  T read(ReadContext context) {
+    final activeCaseId = context.buffer.readVarUint32();
+    return buildValue(activeCaseId, context.readRef());
+  }
 }
diff --git a/dart/packages/fory/lib/src/types/bfloat16.dart b/dart/packages/fory/lib/src/types/bfloat16.dart
new file mode 100644
index 000000000..66d152f85
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/bfloat16.dart
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+
+import 'dart:collection';
+import 'dart:typed_data';
+
+/// Brain floating-point wrapper used by the xlang type system.
+///
+/// [Bfloat16] stores an IEEE 754 bfloat16 payload exactly. Constructing from a
+/// Dart number rounds directly from the incoming IEEE 754 binary64 value to the
+/// closest representable bfloat16 value using round-to-nearest-even semantics.
+final class Bfloat16 implements Comparable<Bfloat16> {
+  final int _bits;
+
+  /// Creates a value directly from IEEE 754 bfloat16 bits.
+  const Bfloat16.fromBits(int bits) : _bits = bits & 0xffff;
+
+  /// Converts [value] to the closest representable bfloat16 value.
+  factory Bfloat16(num value) => Bfloat16.fromDouble(value.toDouble());
+
+  /// Converts [value] to the closest representable bfloat16 value.
+  factory Bfloat16.fromDouble(double value) {
+    final data = ByteData(8)..setFloat64(0, value, Endian.little);
+    final bits = data.getUint64(0, Endian.little);
+    final sign = (bits >> 63) & 0x1;
+    final exponent = (bits >> 52) & 0x7ff;
+    final mantissa = bits & 0x000fffffffffffff;
+
+    if (exponent == 0x7ff) {
+      if (mantissa == 0) {
+        return Bfloat16.fromBits((sign << 15) | 0x7f80);
+      }
+      var payload = (mantissa >> 45) & 0x7f;
+      if (payload == 0) {
+        payload = 0x40;
+      } else {
+        payload |= 0x40;
+      }
+      return Bfloat16.fromBits((sign << 15) | 0x7f80 | payload);
+    }
+
+    final adjustedExponent = exponent - 1023 + 127;
+    if (adjustedExponent >= 0xff) {
+      return Bfloat16.fromBits((sign << 15) | 0x7f80);
+    }
+    if (adjustedExponent <= 0) {
+      if (adjustedExponent < -7) {
+        return Bfloat16.fromBits(sign << 15);
+      }
+      final shifted = (mantissa | (1 << 52)) >> (46 - adjustedExponent);
+      final rounded = (shifted + 1) >> 1;
+      return Bfloat16.fromBits((sign << 15) | rounded);
+    }
+
+    var roundedMantissa = mantissa + 0x0000100000000000;
+    var roundedExponent = adjustedExponent;
+    if ((roundedMantissa & 0x0010000000000000) != 0) {
+      roundedMantissa = 0;
+      roundedExponent += 1;
+      if (roundedExponent >= 0xff) {
+        return Bfloat16.fromBits((sign << 15) | 0x7f80);
+      }
+    }
+    return Bfloat16.fromBits(
+      (sign << 15) | (roundedExponent << 7) | (roundedMantissa >> 45),
+    );
+  }
+
+  /// Returns the raw IEEE 754 bfloat16 bits for this value.
+  int toBits() => _bits;
+
+  /// Expands this bfloat16 value to a Dart [double].
+  double toDouble() {
+    final data = ByteData(4)..setUint32(0, _bits << 16, Endian.little);
+    return data.getFloat32(0, Endian.little);
+  }
+
+  /// Returns the rounded bfloat16 value as a Dart [double].
+  double get value => toDouble();
+
+  /// Returns a bfloat16-rounded sum.
+  Bfloat16 operator +(Object other) => switch (other) {
+        Bfloat16 otherValue => Bfloat16(value + otherValue.value),
+        num otherValue => Bfloat16(value + otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns a bfloat16-rounded difference.
+  Bfloat16 operator -(Object other) => switch (other) {
+        Bfloat16 otherValue => Bfloat16(value - otherValue.value),
+        num otherValue => Bfloat16(value - otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns a bfloat16-rounded product.
+  Bfloat16 operator *(Object other) => switch (other) {
+        Bfloat16 otherValue => Bfloat16(value * otherValue.value),
+        num otherValue => Bfloat16(value * otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns a bfloat16-rounded quotient.
+  Bfloat16 operator /(Object other) => switch (other) {
+        Bfloat16 otherValue => Bfloat16(value / otherValue.value),
+        num otherValue => Bfloat16(value / otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns a bfloat16-rounded remainder.
+  Bfloat16 operator %(Object other) => switch (other) {
+        Bfloat16 otherValue => Bfloat16(value % otherValue.value),
+        num otherValue => Bfloat16(value % otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns the truncated integer quotient, matching Dart `num` semantics.
+  int operator ~/(Object other) => switch (other) {
+        Bfloat16 otherValue => value ~/ otherValue.value,
+        num otherValue => value ~/ otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns the negated bfloat16-rounded value.
+  Bfloat16 operator -() => Bfloat16(-value);
+
+  /// Returns whether this value is strictly less than [other].
+  bool operator <(Object other) => switch (other) {
+        Bfloat16 otherValue => value < otherValue.value,
+        num otherValue => value < otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns whether this value is less than or equal to [other].
+  bool operator <=(Object other) => switch (other) {
+        Bfloat16 otherValue => value <= otherValue.value,
+        num otherValue => value <= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns whether this value is strictly greater than [other].
+  bool operator >(Object other) => switch (other) {
+        Bfloat16 otherValue => value > otherValue.value,
+        num otherValue => value > otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  /// Returns whether this value is greater than or equal to [other].
+  bool operator >=(Object other) => switch (other) {
+        Bfloat16 otherValue => value >= otherValue.value,
+        num otherValue => value >= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Bfloat16.'),
+      };
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Bfloat16 && other._bits == _bits;
+
+  @override
+  int get hashCode => _bits.hashCode;
+
+  /// Compares this value with [other] after expanding both to Dart [double]s.
+  @override
+  int compareTo(Bfloat16 other) => value.compareTo(other.value);
+
+  /// Returns the expanded Dart [double] string form of this value.
+  @override
+  String toString() => value.toString();
+}
+
+/// Fixed-length contiguous storage for [Bfloat16] values.
+///
+/// [Bfloat16List] behaves like a typed-data-style list whose elements are
+/// [Bfloat16] wrappers instead of Dart [double]s. Each element occupies
+/// exactly two bytes, backed by a contiguous [ByteBuffer], so serializers can
+/// process the list as a single dense binary payload.
+final class Bfloat16List extends ListBase<Bfloat16> {
+  /// The number of bytes used by one [Bfloat16] element.
+  static const int bytesPerElement = Uint16List.bytesPerElement;
+
+  final Uint16List _storage;
+
+  /// Creates a zero-initialized list with [length] bfloat16 elements.
+  Bfloat16List(int length) : _storage = Uint16List(length);
+
+  Bfloat16List._(this._storage);
+
+  /// Copies [values] into a new contiguous bfloat16 list.
+  factory Bfloat16List.fromList(Iterable<Bfloat16> values) {
+    final copied = values.toList(growable: false);
+    final result = Bfloat16List(copied.length);
+    for (var index = 0; index < copied.length; index += 1) {
+      result[index] = copied[index];
+    }
+    return result;
+  }
+
+  /// Creates a zero-copy view over [buffer].
+  ///
+  /// [offsetInBytes] must be aligned to [bytesPerElement]. When [length] is
+  /// omitted, the view spans the remaining aligned bfloat16 values.
+  factory Bfloat16List.view(
+    ByteBuffer buffer, [
+    int offsetInBytes = 0,
+    int? length,
+  ]) {
+    return Bfloat16List._(Uint16List.view(buffer, offsetInBytes, length));
+  }
+
+  /// Creates a zero-copy element-range view of [data].
+  ///
+  /// [start] and [end] are measured in [Bfloat16] elements, not bytes.
+  factory Bfloat16List.sublistView(TypedData data, [int start = 0, int? end]) {
+    if (data.lengthInBytes % bytesPerElement != 0) {
+      throw ArgumentError.value(
+        data,
+        'data',
+        'Expected typed data aligned to $bytesPerElement-byte elements.',
+      );
+    }
+    final elementLength = data.lengthInBytes ~/ bytesPerElement;
+    final actualEnd = RangeError.checkValidRange(start, end, elementLength);
+    return Bfloat16List.view(
+      data.buffer,
+      data.offsetInBytes + start * bytesPerElement,
+      actualEnd - start,
+    );
+  }
+
+  /// Returns the shared backing buffer for this list.
+  ByteBuffer get buffer => _storage.buffer;
+
+  /// Returns the size in bytes of each [Bfloat16] element.
+  int get elementSizeInBytes => bytesPerElement;
+
+  /// Returns the byte length of this list view.
+  int get lengthInBytes => _storage.lengthInBytes;
+
+  /// Returns the byte offset of this view in [buffer].
+  int get offsetInBytes => _storage.offsetInBytes;
+
+  /// Returns the number of [Bfloat16] elements in this fixed-length list.
+  @override
+  int get length => _storage.length;
+
+  /// Throws because [Bfloat16List] has a fixed length.
+  @override
+  set length(int newLength) {
+    throw UnsupportedError('Bfloat16List has a fixed length.');
+  }
+
+  /// Reads the element at [index].
+  @override
+  Bfloat16 operator [](int index) => Bfloat16.fromBits(_storage[index]);
+
+  /// Stores [value] at [index].
+  @override
+  void operator []=(int index, Bfloat16 value) {
+    _storage[index] = value.toBits();
+  }
+}
diff --git a/dart/packages/fory/lib/src/types/fixed_ints.dart b/dart/packages/fory/lib/src/types/fixed_ints.dart
deleted file mode 100644
index c9ec2b251..000000000
--- a/dart/packages/fory/lib/src/types/fixed_ints.dart
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.
- */
-
-abstract class _FixedInt implements Comparable<_FixedInt> {
-  final int value;
-
-  const _FixedInt(this.value);
-
-  @override
-  int compareTo(_FixedInt other) => value.compareTo(other.value);
-
-  @override
-  bool operator ==(Object other) =>
-      identical(this, other) ||
-      other.runtimeType == runtimeType &&
-          other is _FixedInt &&
-          other.value == value;
-
-  @override
-  int get hashCode => Object.hash(runtimeType, value);
-
-  @override
-  String toString() => value.toString();
-}
-
-/// Signed 8-bit integer wrapper used by the xlang type system.
-///
-/// Values are normalized to the `[-128, 127]` range at construction time.
-final class Int8 extends _FixedInt {
-  /// Creates a normalized signed 8-bit value.
-  Int8(int value) : super(_normalize(value));
-
-  static int _normalize(int value) => value.toSigned(8);
-}
-
-/// Signed 16-bit integer wrapper used by the xlang type system.
-///
-/// Values are normalized to the `[-32768, 32767]` range at construction time.
-final class Int16 extends _FixedInt {
-  /// Creates a normalized signed 16-bit value.
-  Int16(int value) : super(_normalize(value));
-
-  static int _normalize(int value) => value.toSigned(16);
-}
-
-/// Signed 32-bit integer wrapper used by the xlang type system.
-///
-/// Values are normalized to the signed 32-bit range at construction time.
-final class Int32 extends _FixedInt {
-  /// Creates a normalized signed 32-bit value.
-  Int32(int value) : super(_normalize(value));
-
-  static int _normalize(int value) => value.toSigned(32);
-}
-
-/// Unsigned 8-bit integer wrapper used by the xlang type system.
-final class UInt8 extends _FixedInt {
-  /// Creates a normalized unsigned 8-bit value.
-  UInt8(int value) : super(value.toUnsigned(8));
-}
-
-/// Unsigned 16-bit integer wrapper used by the xlang type system.
-final class UInt16 extends _FixedInt {
-  /// Creates a normalized unsigned 16-bit value.
-  UInt16(int value) : super(value.toUnsigned(16));
-}
-
-/// Unsigned 32-bit integer wrapper used by the xlang type system.
-final class UInt32 extends _FixedInt {
-  /// Creates a normalized unsigned 32-bit value.
-  UInt32(int value) : super(value.toUnsigned(32));
-}
diff --git a/dart/packages/fory/lib/src/types/float16.dart b/dart/packages/fory/lib/src/types/float16.dart
index 90b4aac93..7c3532a9a 100644
--- a/dart/packages/fory/lib/src/types/float16.dart
+++ b/dart/packages/fory/lib/src/types/float16.dart
@@ -17,9 +17,15 @@
  * under the License.
  */
 
+import 'dart:collection';
 import 'dart:typed_data';
 
 /// Half-precision floating-point wrapper used by the xlang type system.
+///
+/// [Float16] stores an IEEE 754 binary16 payload exactly. Constructing from a
+/// Dart number rounds to the closest representable binary16 value, and the
+/// arithmetic operators round every result back to binary16 precision before
+/// returning a new wrapper.
 final class Float16 implements Comparable<Float16> {
   final int _bits;
 
@@ -91,10 +97,100 @@ final class Float16 implements Comparable<Float16> {
           ? (sign == 0 ? double.infinity : double.negativeInfinity)
           : double.nan;
     }
-    final value = (1.0 + mantissa / 1024.0) * (1 << (exponent - 15)).toDouble();
+    final exponentValue = exponent - 15;
+    final scale = exponentValue >= 0
+        ? (1 << exponentValue).toDouble()
+        : 1.0 / (1 << -exponentValue).toDouble();
+    final value = (1.0 + mantissa / 1024.0) * scale;
     return sign == 0 ? value : -value;
   }
 
+  /// Returns the rounded binary16 value as a Dart [double].
+  double get value => toDouble();
+
+  /// Returns a binary16-rounded sum.
+  Float16 operator +(Object other) => switch (other) {
+        Float16 otherValue => Float16(value + otherValue.value),
+        num otherValue => Float16(value + otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns a binary16-rounded difference.
+  Float16 operator -(Object other) => switch (other) {
+        Float16 otherValue => Float16(value - otherValue.value),
+        num otherValue => Float16(value - otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns a binary16-rounded product.
+  Float16 operator *(Object other) => switch (other) {
+        Float16 otherValue => Float16(value * otherValue.value),
+        num otherValue => Float16(value * otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns a binary16-rounded quotient.
+  Float16 operator /(Object other) => switch (other) {
+        Float16 otherValue => Float16(value / otherValue.value),
+        num otherValue => Float16(value / otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns a binary16-rounded remainder.
+  Float16 operator %(Object other) => switch (other) {
+        Float16 otherValue => Float16(value % otherValue.value),
+        num otherValue => Float16(value % otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns the truncated integer quotient, matching Dart `num` semantics.
+  int operator ~/(Object other) => switch (other) {
+        Float16 otherValue => value ~/ otherValue.value,
+        num otherValue => value ~/ otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns the negated binary16-rounded value.
+  Float16 operator -() => Float16(-value);
+
+  /// Returns whether this value is strictly less than [other].
+  bool operator <(Object other) => switch (other) {
+        Float16 otherValue => value < otherValue.value,
+        num otherValue => value < otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns whether this value is less than or equal to [other].
+  bool operator <=(Object other) => switch (other) {
+        Float16 otherValue => value <= otherValue.value,
+        num otherValue => value <= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns whether this value is strictly greater than [other].
+  bool operator >(Object other) => switch (other) {
+        Float16 otherValue => value > otherValue.value,
+        num otherValue => value > otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
+  /// Returns whether this value is greater than or equal to [other].
+  bool operator >=(Object other) => switch (other) {
+        Float16 otherValue => value >= otherValue.value,
+        num otherValue => value >= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float16.'),
+      };
+
   @override
   bool operator ==(Object other) =>
       identical(this, other) || other is Float16 && other._bits == _bits;
@@ -102,9 +198,103 @@ final class Float16 implements Comparable<Float16> {
   @override
   int get hashCode => _bits.hashCode;
 
+  /// Compares this value with [other] after expanding both to Dart [double]s.
+  @override
+  int compareTo(Float16 other) => value.compareTo(other.value);
+
+  /// Returns the expanded Dart [double] string form of this value.
+  @override
+  String toString() => value.toString();
+}
+
+/// Fixed-length contiguous storage for [Float16] values.
+///
+/// [Float16List] behaves like a typed-data-style list whose elements are
+/// [Float16] wrappers instead of Dart [double]s. Each element occupies exactly
+/// two bytes, backed by a contiguous [ByteBuffer], so serializers can write or
+/// read the list without repacking each element.
+final class Float16List extends ListBase<Float16> {
+  /// The number of bytes used by one [Float16] element.
+  static const int bytesPerElement = Uint16List.bytesPerElement;
+
+  final Uint16List _storage;
+
+  /// Creates a zero-initialized list with [length] binary16 elements.
+  Float16List(int length) : _storage = Uint16List(length);
+
+  Float16List._(this._storage);
+
+  /// Copies [values] into a new contiguous binary16 list.
+  factory Float16List.fromList(Iterable<Float16> values) {
+    final copied = values.toList(growable: false);
+    final result = Float16List(copied.length);
+    for (var index = 0; index < copied.length; index += 1) {
+      result[index] = copied[index];
+    }
+    return result;
+  }
+
+  /// Creates a zero-copy view over [buffer].
+  ///
+  /// [offsetInBytes] must be aligned to [bytesPerElement]. When [length] is
+  /// omitted, the view spans the remaining aligned binary16 values.
+  factory Float16List.view(
+    ByteBuffer buffer, [
+    int offsetInBytes = 0,
+    int? length,
+  ]) {
+    return Float16List._(Uint16List.view(buffer, offsetInBytes, length));
+  }
+
+  /// Creates a zero-copy element-range view of [data].
+  ///
+  /// [start] and [end] are measured in [Float16] elements, not bytes.
+  factory Float16List.sublistView(TypedData data, [int start = 0, int? end]) {
+    if (data.lengthInBytes % bytesPerElement != 0) {
+      throw ArgumentError.value(
+        data,
+        'data',
+        'Expected typed data aligned to $bytesPerElement-byte elements.',
+      );
+    }
+    final elementLength = data.lengthInBytes ~/ bytesPerElement;
+    final actualEnd = RangeError.checkValidRange(start, end, elementLength);
+    return Float16List.view(
+      data.buffer,
+      data.offsetInBytes + start * bytesPerElement,
+      actualEnd - start,
+    );
+  }
+
+  /// Returns the shared backing buffer for this list.
+  ByteBuffer get buffer => _storage.buffer;
+
+  /// Returns the size in bytes of each [Float16] element.
+  int get elementSizeInBytes => bytesPerElement;
+
+  /// Returns the byte length of this list view.
+  int get lengthInBytes => _storage.lengthInBytes;
+
+  /// Returns the byte offset of this view in [buffer].
+  int get offsetInBytes => _storage.offsetInBytes;
+
+  /// Returns the number of [Float16] elements in this fixed-length list.
+  @override
+  int get length => _storage.length;
+
+  /// Throws because [Float16List] has a fixed length.
   @override
-  int compareTo(Float16 other) => toDouble().compareTo(other.toDouble());
+  set length(int newLength) {
+    throw UnsupportedError('Float16List has a fixed length.');
+  }
 
+  /// Reads the element at [index].
   @override
-  String toString() => toDouble().toString();
+  Float16 operator [](int index) => Float16.fromBits(_storage[index]);
+
+  /// Stores [value] at [index].
+  @override
+  void operator []=(int index, Float16 value) {
+    _storage[index] = value.toBits();
+  }
 }
diff --git a/dart/packages/fory/lib/src/types/float32.dart b/dart/packages/fory/lib/src/types/float32.dart
index 94ca65c01..532d8d66c 100644
--- a/dart/packages/fory/lib/src/types/float32.dart
+++ b/dart/packages/fory/lib/src/types/float32.dart
@@ -20,22 +20,126 @@
 import 'dart:typed_data';
 
 /// Single-precision floating-point wrapper used by the xlang type system.
+///
+/// [Float32] stores an IEEE 754 binary32 payload exactly. Constructing from a
+/// Dart number rounds to the closest representable binary32 value, and the
+/// arithmetic operators round every result back to binary32 precision before
+/// returning a new wrapper.
 final class Float32 implements Comparable<Float32> {
-  /// The normalized 32-bit floating-point value as a Dart [double].
-  final double value;
+  final int _bits;
+
+  /// Creates a value directly from IEEE 754 binary32 bits.
+  const Float32.fromBits(int bits) : _bits = bits & 0xffffffff;
 
   /// Creates a value rounded to IEEE 754 binary32 precision.
-  Float32(num value) : value = (Float32List(1)..[0] = value.toDouble())[0];
+  factory Float32(num value) => Float32.fromDouble(value.toDouble());
+
+  /// Converts [value] to the closest representable binary32 value.
+  factory Float32.fromDouble(double value) {
+    final data = ByteData(4)..setFloat32(0, value, Endian.little);
+    return Float32.fromBits(data.getUint32(0, Endian.little));
+  }
+
+  /// Returns the raw IEEE 754 binary32 bits for this value.
+  int toBits() => _bits;
+
+  /// Expands this binary32 value to a Dart [double].
+  double toDouble() {
+    final data = ByteData(4)..setUint32(0, _bits, Endian.little);
+    return data.getFloat32(0, Endian.little);
+  }
+
+  /// Returns the rounded binary32 value as a Dart [double].
+  double get value => toDouble();
+
+  /// Returns a binary32-rounded sum.
+  Float32 operator +(Object other) => switch (other) {
+        Float32 otherValue => Float32(value + otherValue.value),
+        num otherValue => Float32(value + otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns a binary32-rounded difference.
+  Float32 operator -(Object other) => switch (other) {
+        Float32 otherValue => Float32(value - otherValue.value),
+        num otherValue => Float32(value - otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns a binary32-rounded product.
+  Float32 operator *(Object other) => switch (other) {
+        Float32 otherValue => Float32(value * otherValue.value),
+        num otherValue => Float32(value * otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns a binary32-rounded quotient.
+  Float32 operator /(Object other) => switch (other) {
+        Float32 otherValue => Float32(value / otherValue.value),
+        num otherValue => Float32(value / otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns a binary32-rounded remainder.
+  Float32 operator %(Object other) => switch (other) {
+        Float32 otherValue => Float32(value % otherValue.value),
+        num otherValue => Float32(value % otherValue.toDouble()),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns the truncated integer quotient, matching Dart `num` semantics.
+  int operator ~/(Object other) => switch (other) {
+        Float32 otherValue => value ~/ otherValue.value,
+        num otherValue => value ~/ otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  /// Returns the negated binary32-rounded value.
+  Float32 operator -() => Float32(-value);
+
+  bool operator <(Object other) => switch (other) {
+        Float32 otherValue => value < otherValue.value,
+        num otherValue => value < otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        Float32 otherValue => value <= otherValue.value,
+        num otherValue => value <= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        Float32 otherValue => value > otherValue.value,
+        num otherValue => value > otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        Float32 otherValue => value >= otherValue.value,
+        num otherValue => value >= otherValue.toDouble(),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected a num or Float32.'),
+      };
 
   @override
   int compareTo(Float32 other) => value.compareTo(other.value);
 
   @override
   bool operator ==(Object other) =>
-      identical(this, other) || other is Float32 && other.value == value;
+      identical(this, other) || other is Float32 && other._bits == _bits;
 
   @override
-  int get hashCode => value.hashCode;
+  int get hashCode => _bits.hashCode;
 
   @override
   String toString() => value.toString();
diff --git a/dart/packages/fory/lib/src/types/int16.dart b/dart/packages/fory/lib/src/types/int16.dart
new file mode 100644
index 000000000..ddef75b88
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/int16.dart
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/// Signed 16-bit integer wrapper used by the xlang type system.
+///
+/// The constructor truncates [value] to 16 bits using two's-complement
+/// semantics, and arithmetic operators normalize every result back to the
+/// signed 16-bit range.
+final class Int16 implements Comparable<Int16> {
+  /// The normalized signed 16-bit value.
+  final int value;
+
+  /// Creates a signed 16-bit value by truncating [value] to 16 bits.
+  Int16(int value) : value = value.toSigned(16);
+
+  @override
+  int compareTo(Int16 other) => value.compareTo(other.value);
+
+  Int16 operator +(Object other) => switch (other) {
+        int otherValue => Int16(value + otherValue),
+        Int16 otherValue => Int16(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator -(Object other) => switch (other) {
+        int otherValue => Int16(value - otherValue),
+        Int16 otherValue => Int16(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator *(Object other) => switch (other) {
+        int otherValue => Int16(value * otherValue),
+        Int16 otherValue => Int16(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator ~/(Object other) => switch (other) {
+        int otherValue => Int16(value ~/ otherValue),
+        Int16 otherValue => Int16(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator %(Object other) => switch (other) {
+        int otherValue => Int16(value % otherValue),
+        Int16 otherValue => Int16(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Int16 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator -() => Int16(-value);
+
+  Int16 operator ~() => Int16(~value);
+
+  Int16 operator &(Object other) => switch (other) {
+        int otherValue => Int16(value & otherValue),
+        Int16 otherValue => Int16(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator |(Object other) => switch (other) {
+        int otherValue => Int16(value | otherValue),
+        Int16 otherValue => Int16(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator ^(Object other) => switch (other) {
+        int otherValue => Int16(value ^ otherValue),
+        Int16 otherValue => Int16(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  Int16 operator <<(int shift) => Int16(value << shift);
+
+  Int16 operator >>(int shift) => Int16(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Int16 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Int16 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Int16 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Int16 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int16.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Int16 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/int32.dart b/dart/packages/fory/lib/src/types/int32.dart
new file mode 100644
index 000000000..4bfa350f1
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/int32.dart
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/// Signed 32-bit integer wrapper used by the xlang type system.
+///
+/// This wrapper preserves xlang `int32` and `varint32` semantics on the Dart
+/// side. Arithmetic and bitwise operators normalize every result back to the
+/// signed 32-bit range.
+final class Int32 implements Comparable<Int32> {
+  /// The normalized signed 32-bit value.
+  final int value;
+
+  /// Creates a signed 32-bit value by truncating [value] to 32 bits.
+  Int32(int value) : value = value.toSigned(32);
+
+  @override
+  int compareTo(Int32 other) => value.compareTo(other.value);
+
+  Int32 operator +(Object other) => switch (other) {
+        int otherValue => Int32(value + otherValue),
+        Int32 otherValue => Int32(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator -(Object other) => switch (other) {
+        int otherValue => Int32(value - otherValue),
+        Int32 otherValue => Int32(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator *(Object other) => switch (other) {
+        int otherValue => Int32(value * otherValue),
+        Int32 otherValue => Int32(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator ~/(Object other) => switch (other) {
+        int otherValue => Int32(value ~/ otherValue),
+        Int32 otherValue => Int32(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator %(Object other) => switch (other) {
+        int otherValue => Int32(value % otherValue),
+        Int32 otherValue => Int32(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Int32 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator -() => Int32(-value);
+
+  Int32 operator ~() => Int32(~value);
+
+  Int32 operator &(Object other) => switch (other) {
+        int otherValue => Int32(value & otherValue),
+        Int32 otherValue => Int32(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator |(Object other) => switch (other) {
+        int otherValue => Int32(value | otherValue),
+        Int32 otherValue => Int32(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator ^(Object other) => switch (other) {
+        int otherValue => Int32(value ^ otherValue),
+        Int32 otherValue => Int32(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  Int32 operator <<(int shift) => Int32(value << shift);
+
+  Int32 operator >>(int shift) => Int32(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Int32 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Int32 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Int32 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Int32 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Int32.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Int32 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/int8.dart b/dart/packages/fory/lib/src/types/int8.dart
new file mode 100644
index 000000000..3d9531e86
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/int8.dart
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+/// Signed 8-bit integer wrapper used by the xlang type system.
+///
+/// The constructor truncates [value] to 8 bits using two's-complement
+/// semantics, so arithmetic, shifts, and bitwise operators behave like native
+/// 8-bit machine operations. Operators that produce wrapped integers return a
+/// new normalized [Int8]; `/` returns a Dart [double].
+final class Int8 implements Comparable<Int8> {
+  /// The normalized signed 8-bit value.
+  final int value;
+
+  /// Creates a signed 8-bit value by truncating [value] to 8 bits.
+  Int8(int value) : value = value.toSigned(8);
+
+  @override
+  int compareTo(Int8 other) => value.compareTo(other.value);
+
+  /// Returns a normalized sum.
+  Int8 operator +(Object other) => switch (other) {
+        int otherValue => Int8(value + otherValue),
+        Int8 otherValue => Int8(value + otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized difference.
+  Int8 operator -(Object other) => switch (other) {
+        int otherValue => Int8(value - otherValue),
+        Int8 otherValue => Int8(value - otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized product.
+  Int8 operator *(Object other) => switch (other) {
+        int otherValue => Int8(value * otherValue),
+        Int8 otherValue => Int8(value * otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized integer quotient.
+  Int8 operator ~/(Object other) => switch (other) {
+        int otherValue => Int8(value ~/ otherValue),
+        Int8 otherValue => Int8(value ~/ otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized remainder.
+  Int8 operator %(Object other) => switch (other) {
+        int otherValue => Int8(value % otherValue),
+        Int8 otherValue => Int8(value % otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a Dart [double] quotient.
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Int8 otherValue => value / otherValue.value,
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns the normalized negated value.
+  Int8 operator -() => Int8(-value);
+
+  /// Returns the normalized bitwise inverse.
+  Int8 operator ~() => Int8(~value);
+
+  /// Returns a normalized bitwise AND.
+  Int8 operator &(Object other) => switch (other) {
+        int otherValue => Int8(value & otherValue),
+        Int8 otherValue => Int8(value & otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized bitwise OR.
+  Int8 operator |(Object other) => switch (other) {
+        int otherValue => Int8(value | otherValue),
+        Int8 otherValue => Int8(value | otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized bitwise XOR.
+  Int8 operator ^(Object other) => switch (other) {
+        int otherValue => Int8(value ^ otherValue),
+        Int8 otherValue => Int8(value ^ otherValue.value),
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns a normalized left shift.
+  Int8 operator <<(int shift) => Int8(value << shift);
+
+  /// Returns a normalized arithmetic right shift.
+  Int8 operator >>(int shift) => Int8(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Int8 otherValue => value < otherValue.value,
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Int8 otherValue => value <= otherValue.value,
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Int8 otherValue => value > otherValue.value,
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Int8 otherValue => value >= otherValue.value,
+        _ =>
+          throw ArgumentError.value(other, 'other', 'Expected an int or Int8.'),
+      };
+
+  /// Returns the normalized integer value as a Dart [int].
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Int8 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/timestamp.dart b/dart/packages/fory/lib/src/types/timestamp.dart
index e408502ba..060b529f2 100644
--- a/dart/packages/fory/lib/src/types/timestamp.dart
+++ b/dart/packages/fory/lib/src/types/timestamp.dart
@@ -32,8 +32,12 @@ final class Timestamp implements Comparable<Timestamp> {
   factory Timestamp.fromDateTime(DateTime value) {
     final utcValue = value.toUtc();
     final microseconds = utcValue.microsecondsSinceEpoch;
-    final seconds = microseconds ~/ Duration.microsecondsPerSecond;
-    final micros = microseconds % Duration.microsecondsPerSecond;
+    var seconds = microseconds ~/ Duration.microsecondsPerSecond;
+    var micros = microseconds.remainder(Duration.microsecondsPerSecond);
+    if (micros < 0) {
+      micros += Duration.microsecondsPerSecond;
+      seconds -= 1;
+    }
     return Timestamp(seconds, micros * 1000);
   }
 
diff --git a/dart/packages/fory/lib/src/types/uint16.dart b/dart/packages/fory/lib/src/types/uint16.dart
new file mode 100644
index 000000000..1a92db2e6
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/uint16.dart
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/// Unsigned 16-bit integer wrapper used by the xlang type system.
+///
+/// The constructor truncates [value] to 16 bits using unsigned wrap-around
+/// semantics. Arithmetic and bitwise operators normalize their results back to
+/// the `0..65535` range.
+final class Uint16 implements Comparable<Uint16> {
+  /// The normalized unsigned 16-bit value.
+  final int value;
+
+  /// Creates an unsigned 16-bit value by truncating [value] to 16 bits.
+  Uint16(int value) : value = value.toUnsigned(16);
+
+  @override
+  int compareTo(Uint16 other) => value.compareTo(other.value);
+
+  Uint16 operator +(Object other) => switch (other) {
+        int otherValue => Uint16(value + otherValue),
+        Uint16 otherValue => Uint16(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator -(Object other) => switch (other) {
+        int otherValue => Uint16(value - otherValue),
+        Uint16 otherValue => Uint16(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator *(Object other) => switch (other) {
+        int otherValue => Uint16(value * otherValue),
+        Uint16 otherValue => Uint16(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator ~/(Object other) => switch (other) {
+        int otherValue => Uint16(value ~/ otherValue),
+        Uint16 otherValue => Uint16(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator %(Object other) => switch (other) {
+        int otherValue => Uint16(value % otherValue),
+        Uint16 otherValue => Uint16(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Uint16 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator -() => Uint16(-value);
+
+  Uint16 operator ~() => Uint16(~value);
+
+  Uint16 operator &(Object other) => switch (other) {
+        int otherValue => Uint16(value & otherValue),
+        Uint16 otherValue => Uint16(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator |(Object other) => switch (other) {
+        int otherValue => Uint16(value | otherValue),
+        Uint16 otherValue => Uint16(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator ^(Object other) => switch (other) {
+        int otherValue => Uint16(value ^ otherValue),
+        Uint16 otherValue => Uint16(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  Uint16 operator <<(int shift) => Uint16(value << shift);
+
+  Uint16 operator >>(int shift) => Uint16(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Uint16 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Uint16 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Uint16 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Uint16 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint16.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Uint16 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/uint32.dart b/dart/packages/fory/lib/src/types/uint32.dart
new file mode 100644
index 000000000..b8b81c62f
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/uint32.dart
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/// Unsigned 32-bit integer wrapper used by the xlang type system.
+///
+/// This wrapper preserves xlang `uint32` and `varuint32` semantics on the Dart
+/// side. Arithmetic and bitwise operators normalize every result back to the
+/// unsigned 32-bit range.
+final class Uint32 implements Comparable<Uint32> {
+  /// The normalized unsigned 32-bit value.
+  final int value;
+
+  /// Creates an unsigned 32-bit value by truncating [value] to 32 bits.
+  Uint32(int value) : value = value.toUnsigned(32);
+
+  @override
+  int compareTo(Uint32 other) => value.compareTo(other.value);
+
+  Uint32 operator +(Object other) => switch (other) {
+        int otherValue => Uint32(value + otherValue),
+        Uint32 otherValue => Uint32(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator -(Object other) => switch (other) {
+        int otherValue => Uint32(value - otherValue),
+        Uint32 otherValue => Uint32(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator *(Object other) => switch (other) {
+        int otherValue => Uint32(value * otherValue),
+        Uint32 otherValue => Uint32(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator ~/(Object other) => switch (other) {
+        int otherValue => Uint32(value ~/ otherValue),
+        Uint32 otherValue => Uint32(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator %(Object other) => switch (other) {
+        int otherValue => Uint32(value % otherValue),
+        Uint32 otherValue => Uint32(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Uint32 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator -() => Uint32(-value);
+
+  Uint32 operator ~() => Uint32(~value);
+
+  Uint32 operator &(Object other) => switch (other) {
+        int otherValue => Uint32(value & otherValue),
+        Uint32 otherValue => Uint32(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator |(Object other) => switch (other) {
+        int otherValue => Uint32(value | otherValue),
+        Uint32 otherValue => Uint32(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator ^(Object other) => switch (other) {
+        int otherValue => Uint32(value ^ otherValue),
+        Uint32 otherValue => Uint32(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  Uint32 operator <<(int shift) => Uint32(value << shift);
+
+  Uint32 operator >>(int shift) => Uint32(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Uint32 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Uint32 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Uint32 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Uint32 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint32.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Uint32 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/uint64.dart b/dart/packages/fory/lib/src/types/uint64.dart
new file mode 100644
index 000000000..1c335e84f
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/uint64.dart
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+/// Unsigned 64-bit integer wrapper used by the xlang type system.
+///
+/// The constructor truncates [value] to 64 bits using unsigned wrap-around
+/// semantics. This wrapper is the default Dart carrier for root and untyped
+/// xlang `uint64`, `varuint64`, and `taggeduint64` payloads; declared `int`
+/// fields annotated with `@Uint64Type(...)` still read back as plain Dart
+/// [int] values through generated conversion paths.
+final class Uint64 implements Comparable<Uint64> {
+  /// The normalized unsigned 64-bit value.
+  final int value;
+
+  /// Creates an unsigned 64-bit value by truncating [value] to 64 bits.
+  Uint64(int value) : value = value.toUnsigned(64);
+
+  @override
+  int compareTo(Uint64 other) => value.compareTo(other.value);
+
+  Uint64 operator +(Object other) => switch (other) {
+        int otherValue => Uint64(value + otherValue),
+        Uint64 otherValue => Uint64(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator -(Object other) => switch (other) {
+        int otherValue => Uint64(value - otherValue),
+        Uint64 otherValue => Uint64(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator *(Object other) => switch (other) {
+        int otherValue => Uint64(value * otherValue),
+        Uint64 otherValue => Uint64(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator ~/(Object other) => switch (other) {
+        int otherValue => Uint64(value ~/ otherValue),
+        Uint64 otherValue => Uint64(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator %(Object other) => switch (other) {
+        int otherValue => Uint64(value % otherValue),
+        Uint64 otherValue => Uint64(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Uint64 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator -() => Uint64(-value);
+
+  Uint64 operator ~() => Uint64(~value);
+
+  Uint64 operator &(Object other) => switch (other) {
+        int otherValue => Uint64(value & otherValue),
+        Uint64 otherValue => Uint64(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator |(Object other) => switch (other) {
+        int otherValue => Uint64(value | otherValue),
+        Uint64 otherValue => Uint64(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator ^(Object other) => switch (other) {
+        int otherValue => Uint64(value ^ otherValue),
+        Uint64 otherValue => Uint64(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  Uint64 operator <<(int shift) => Uint64(value << shift);
+
+  Uint64 operator >>(int shift) => Uint64(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Uint64 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Uint64 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Uint64 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Uint64 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint64.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Uint64 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/lib/src/types/uint8.dart b/dart/packages/fory/lib/src/types/uint8.dart
new file mode 100644
index 000000000..ee88c0540
--- /dev/null
+++ b/dart/packages/fory/lib/src/types/uint8.dart
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/// Unsigned 8-bit integer wrapper used by the xlang type system.
+///
+/// The constructor truncates [value] to 8 bits using unsigned wrap-around
+/// semantics. Arithmetic and bitwise operators normalize their results back to
+/// the `0..255` range.
+final class Uint8 implements Comparable<Uint8> {
+  /// The normalized unsigned 8-bit value.
+  final int value;
+
+  /// Creates an unsigned 8-bit value by truncating [value] to 8 bits.
+  Uint8(int value) : value = value.toUnsigned(8);
+
+  @override
+  int compareTo(Uint8 other) => value.compareTo(other.value);
+
+  Uint8 operator +(Object other) => switch (other) {
+        int otherValue => Uint8(value + otherValue),
+        Uint8 otherValue => Uint8(value + otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator -(Object other) => switch (other) {
+        int otherValue => Uint8(value - otherValue),
+        Uint8 otherValue => Uint8(value - otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator *(Object other) => switch (other) {
+        int otherValue => Uint8(value * otherValue),
+        Uint8 otherValue => Uint8(value * otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator ~/(Object other) => switch (other) {
+        int otherValue => Uint8(value ~/ otherValue),
+        Uint8 otherValue => Uint8(value ~/ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator %(Object other) => switch (other) {
+        int otherValue => Uint8(value % otherValue),
+        Uint8 otherValue => Uint8(value % otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  double operator /(Object other) => switch (other) {
+        int otherValue => value / otherValue,
+        Uint8 otherValue => value / otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator -() => Uint8(-value);
+
+  Uint8 operator ~() => Uint8(~value);
+
+  Uint8 operator &(Object other) => switch (other) {
+        int otherValue => Uint8(value & otherValue),
+        Uint8 otherValue => Uint8(value & otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator |(Object other) => switch (other) {
+        int otherValue => Uint8(value | otherValue),
+        Uint8 otherValue => Uint8(value | otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator ^(Object other) => switch (other) {
+        int otherValue => Uint8(value ^ otherValue),
+        Uint8 otherValue => Uint8(value ^ otherValue.value),
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  Uint8 operator <<(int shift) => Uint8(value << shift);
+
+  Uint8 operator >>(int shift) => Uint8(value >> shift);
+
+  bool operator <(Object other) => switch (other) {
+        int otherValue => value < otherValue,
+        Uint8 otherValue => value < otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  bool operator <=(Object other) => switch (other) {
+        int otherValue => value <= otherValue,
+        Uint8 otherValue => value <= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  bool operator >(Object other) => switch (other) {
+        int otherValue => value > otherValue,
+        Uint8 otherValue => value > otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  bool operator >=(Object other) => switch (other) {
+        int otherValue => value >= otherValue,
+        Uint8 otherValue => value >= otherValue.value,
+        _ => throw ArgumentError.value(
+            other, 'other', 'Expected an int or Uint8.'),
+      };
+
+  int toInt() => value;
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is Uint8 && other.value == value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/dart/packages/fory/test/buffer_test.dart b/dart/packages/fory/test/buffer_test.dart
index 762b3a905..4c972e5a9 100644
--- a/dart/packages/fory/test/buffer_test.dart
+++ b/dart/packages/fory/test/buffer_test.dart
@@ -17,60 +17,452 @@
  * under the License.
  */
 
+import 'dart:typed_data';
+
 import 'package:fory/fory.dart';
-import 'package:fory/src/codegen/generated_support.dart';
 import 'package:test/test.dart';
 
 void main() {
   group('Buffer', () {
-    test('round-trips primitive values', () {
+    test('round-trips fixed-width primitives, 16-bit floats, and bytes', () {
       final buffer = Buffer();
+      final half = Float16(-2.5);
+      final brain = Bfloat16(-3.5);
+
+      buffer.writeBool(false);
       buffer.writeBool(true);
-      buffer.writeInt16(-7);
-      buffer.writeInt32(42);
-      buffer.writeInt64(123456789);
+      buffer.writeByte(-128);
+      buffer.writeUint8(255);
+      buffer.writeInt16(-32768);
+      buffer.writeUint16(0xffff);
+      buffer.writeInt32(-0x80000000);
+      buffer.writeUint32(0xffffffff);
+      buffer.writeInt64(-0x8000000000000000);
+      buffer.writeUint64(0xffffffffffffffff);
+      buffer.writeFloat16(half);
+      buffer.writeBfloat16(brain);
       buffer.writeFloat32(1.5);
       buffer.writeFloat64(2.5);
-      buffer.writeVarInt32(-9);
-      buffer.writeVarUint32(300);
-      buffer.writeVarInt64(-17);
-      buffer.writeVarUint64(9000);
+      buffer.writeBytes([1, 2, 3, 4]);
 
+      expect(buffer.readBool(), isFalse);
       expect(buffer.readBool(), isTrue);
-      expect(buffer.readInt16(), equals(-7));
-      expect(buffer.readInt32(), equals(42));
-      expect(buffer.readInt64(), equals(123456789));
+      expect(buffer.readByte(), equals(-128));
+      expect(buffer.readUint8(), equals(255));
+      expect(buffer.readInt16(), equals(-32768));
+      expect(buffer.readUint16(), equals(0xffff));
+      expect(buffer.readInt32(), equals(-0x80000000));
+      expect(buffer.readUint32(), equals(0xffffffff));
+      expect(buffer.readInt64(), equals(-0x8000000000000000));
+      expect(buffer.readUint64(), equals(0xffffffffffffffff));
+      expect(buffer.readFloat16(), equals(half));
+      expect(buffer.readBfloat16(), equals(brain));
       expect(buffer.readFloat32(), closeTo(1.5, 0.0001));
       expect(buffer.readFloat64(), equals(2.5));
-      expect(buffer.readVarInt32(), equals(-9));
-      expect(buffer.readVarUint32(), equals(300));
-      expect(buffer.readVarInt64(), equals(-17));
-      expect(buffer.readVarUint64(), equals(9000));
+      expect(buffer.readBytes(4), orderedEquals([1, 2, 3, 4]));
+      expect(buffer.readableBytes, equals(0));
     });
 
-    test('tagged uint64 sign extension regression', () {
+    test('grows, shares toBytes storage, and preserves content', () {
+      final buffer = Buffer(1);
+      final payload = List<int>.generate(600, (index) => index & 0xff);
+
+      buffer.writeBytes(payload);
+
+      expect(buffer.toBytes(), orderedEquals(payload));
+
+      final written = buffer.toBytes();
+      written[0] = 99;
+      written[written.length - 1] = 7;
+
+      expect(buffer.toBytes().first, equals(99));
+      expect(buffer.toBytes().last, equals(7));
+    });
+
+    test(
+      'wrap constructor, wrap method, read views, copyBytes, skip, and clear manage indices',
+      () {
+        final backing = Uint8List.fromList([10, 20, 30, 40, 50]);
+        final buffer = Buffer.wrap(backing);
+
+        expect(buffer.readableBytes, equals(5));
+
+        buffer.skip(1);
+        expect(buffer.readableBytes, equals(4));
+
+        final view = buffer.readBytes(2);
+        expect(view, orderedEquals([20, 30]));
+        view[0] = 99;
+        expect(backing[1], equals(99));
+
+        final copy = buffer.copyBytes(1);
+        expect(copy, orderedEquals([40]));
+        copy[0] = 7;
+        expect(backing[3], equals(40));
+
+        expect(buffer.readUint8(), equals(50));
+        expect(buffer.readableBytes, equals(0));
+
+        buffer.clear();
+        expect(buffer.readableBytes, equals(0));
+        buffer.writeBytes([1, 2]);
+        expect(buffer.toBytes(), orderedEquals([1, 2]));
+
+        final replacement = Uint8List.fromList([7, 8, 9]);
+        buffer.wrap(replacement);
+        expect(buffer.readableBytes, equals(3));
+        expect(buffer.readBytes(3), orderedEquals([7, 8, 9]));
+      },
+    );
+
+    test('round-trips UTF-8 strings with length prefixes', () {
       final buffer = Buffer();
-      final testValue = 0x7FFFFFFF;
+      const ascii = 'Apache Fory';
+      const unicode = '你好,Fory🙂';
+
+      buffer.writeUtf8('');
+      buffer.writeUtf8(ascii);
+      buffer.writeUtf8(unicode);
+
+      expect(buffer.readUtf8(), equals(''));
+      expect(buffer.readUtf8(), equals(ascii));
+      expect(buffer.readUtf8(), equals(unicode));
+      expect(buffer.readableBytes, equals(0));
+    });
+
+    test('round-trips varuint32 boundary values with Java-aligned lengths', () {
+      const cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 1),
+        (bytes: 1, value: 1 << 6),
+        (bytes: 2, value: 1 << 7),
+        (bytes: 2, value: 1 << 13),
+        (bytes: 3, value: 1 << 14),
+        (bytes: 3, value: 1 << 20),
+        (bytes: 4, value: 1 << 21),
+        (bytes: 4, value: 1 << 27),
+        (bytes: 5, value: 1 << 28),
+        (bytes: 5, value: 0x7fffffff),
+        (bytes: 5, value: 0xffffffff),
+      ];
+
+      for (final testCase in cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarUint32(value),
+          read: (buffer) => buffer.readVarUint32(),
+          cursorRead: (cursor) => cursor.readVarUint32(),
+        );
+      }
+    });
 
-      buffer.writeTaggedUint64(testValue);
-      buffer.wrap(buffer.toBytes());
+    test('round-trips varint32 boundary values with Java-aligned lengths', () {
+      const cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 1),
+        (bytes: 1, value: -1),
+        (bytes: 1, value: 1 << 5),
+        (bytes: 1, value: -64),
+        (bytes: 2, value: 1 << 6),
+        (bytes: 2, value: 1 << 12),
+        (bytes: 2, value: -8192),
+        (bytes: 3, value: 1 << 13),
+        (bytes: 3, value: 1 << 19),
+        (bytes: 3, value: -1048576),
+        (bytes: 4, value: 1 << 20),
+        (bytes: 4, value: 1 << 26),
+        (bytes: 4, value: -134217728),
+        (bytes: 5, value: 1 << 27),
+        (bytes: 5, value: 0x7fffffff),
+        (bytes: 5, value: -0x80000000),
+      ];
 
-      final result = buffer.readTaggedUint64();
+      for (final testCase in cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarInt32(value),
+          read: (buffer) => buffer.readVarInt32(),
+          cursorRead: (cursor) => cursor.readVarInt32(),
+        );
+      }
+    });
+
+    test('round-trips varuint64 boundary values with Java-aligned lengths', () {
+      const cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 1),
+        (bytes: 1, value: 1 << 6),
+        (bytes: 2, value: 1 << 7),
+        (bytes: 2, value: 1 << 13),
+        (bytes: 3, value: 1 << 14),
+        (bytes: 3, value: 1 << 20),
+        (bytes: 4, value: 1 << 21),
+        (bytes: 4, value: 1 << 27),
+        (bytes: 5, value: 1 << 28),
+        (bytes: 5, value: 1 << 34),
+        (bytes: 6, value: 1 << 35),
+        (bytes: 6, value: 1 << 41),
+        (bytes: 7, value: 1 << 42),
+        (bytes: 7, value: 1 << 48),
+        (bytes: 8, value: 1 << 49),
+        (bytes: 8, value: 1 << 55),
+        (bytes: 9, value: 1 << 56),
+        (bytes: 9, value: 1 << 62),
+        (bytes: 9, value: 0x7fffffffffffffff),
+        (bytes: 9, value: -0x8000000000000000),
+        (bytes: 9, value: 0xffffffffffffffff),
+      ];
+
+      for (final testCase in cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarUint64(value),
+          read: (buffer) => buffer.readVarUint64(),
+          cursorRead: (cursor) => cursor.readVarUint64(),
+        );
+      }
+    });
+
+    test('round-trips varint64 boundary values with Java-aligned lengths', () {
+      const cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 1),
+        (bytes: 1, value: -1),
+        (bytes: 1, value: -64),
+        (bytes: 2, value: 1 << 6),
+        (bytes: 2, value: -128),
+        (bytes: 3, value: 1 << 13),
+        (bytes: 3, value: -16384),
+        (bytes: 4, value: 1 << 20),
+        (bytes: 4, value: -2097152),
+        (bytes: 5, value: 1 << 27),
+        (bytes: 5, value: -268435456),
+        (bytes: 6, value: 1 << 34),
+        (bytes: 6, value: -34359738368),
+        (bytes: 7, value: 1 << 42),
+        (bytes: 7, value: -4398046511104),
+        (bytes: 8, value: 1 << 49),
+        (bytes: 8, value: -562949953421312),
+        (bytes: 9, value: 1 << 55),
+        (bytes: 9, value: -72057594037927936),
+        (bytes: 9, value: 0x7fffffffffffffff),
+        (bytes: 9, value: -0x8000000000000000),
+      ];
+
+      for (final testCase in cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarInt64(value),
+          read: (buffer) => buffer.readVarInt64(),
+          cursorRead: (cursor) => cursor.readVarInt64(),
+        );
+      }
+    });
+
+    test('round-trips tagged int64 boundary values with Java-aligned lengths',
+        () {
+      const cases = <({int bytes, int value})>[
+        (bytes: 4, value: -0x40000000),
+        (bytes: 4, value: -1),
+        (bytes: 4, value: 0),
+        (bytes: 4, value: 1),
+        (bytes: 4, value: 1 << 28),
+        (bytes: 4, value: 0x3fffffff),
+        (bytes: 9, value: -0x40000001),
+        (bytes: 9, value: 0x40000000),
+        (bytes: 9, value: 0x7fffffffffffffff),
+        (bytes: 9, value: -0x8000000000000000),
+      ];
+
+      for (final testCase in cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeTaggedInt64(value),
+          read: (buffer) => buffer.readTaggedInt64(),
+          cursorRead: (cursor) => cursor.readTaggedInt64(),
+        );
+      }
+    });
+
+    test(
+      'round-trips tagged uint64 boundary values with Java-aligned lengths',
+      () {
+        const cases = <({int bytes, int value})>[
+          (bytes: 4, value: 0),
+          (bytes: 4, value: 1),
+          (bytes: 4, value: 1 << 30),
+          (bytes: 4, value: 0x7fffffff),
+          (bytes: 9, value: 0x80000000),
+          (bytes: 9, value: 0x100000000),
+          (bytes: 9, value: 0x7fffffffffffffff),
+          (bytes: 9, value: -0x8000000000000000),
+          (bytes: 9, value: 0xffffffffffffffff),
+        ];
 
-      expect(result, equals(testValue));
+        for (final testCase in cases) {
+          _expectEncodedIntRoundTrip(
+            value: testCase.value,
+            expectedBytes: testCase.bytes,
+            write: (buffer, value) => buffer.writeTaggedUint64(value),
+            read: (buffer) => buffer.readTaggedUint64(),
+            cursorRead: (cursor) => cursor.readTaggedUint64(),
+          );
+        }
+      },
+    );
+
+    test('round-trips small varuint helpers', () {
+      const small7Cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 1),
+        (bytes: 1, value: 127),
+        (bytes: 3, value: 0x7fff),
+        (bytes: 5, value: 0xffffffff),
+      ];
+      const small14Cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 2, value: 1 << 7),
+        (bytes: 3, value: 1 << 14),
+        (bytes: 5, value: 0xffffffff),
+      ];
+      const small36Cases = <({int bytes, int value})>[
+        (bytes: 1, value: 0),
+        (bytes: 1, value: 10),
+        (bytes: 3, value: 0x7fff),
+        (bytes: 5, value: 0x7fffffff),
+        (bytes: 6, value: 0xfffffffff),
+      ];
+
+      for (final testCase in small7Cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarUint32Small7(value),
+          read: (buffer) => buffer.readVarUint32Small7(),
+        );
+      }
+      for (final testCase in small14Cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarUint32Small14(value),
+          read: (buffer) => buffer.readVarUint32Small14(),
+        );
+      }
+      for (final testCase in small36Cases) {
+        _expectEncodedIntRoundTrip(
+          value: testCase.value,
+          expectedBytes: testCase.bytes,
+          write: (buffer, value) => buffer.writeVarUint36Small(value),
+          read: (buffer) => buffer.readVarUint36Small(),
+        );
+      }
     });
 
-    test('GeneratedReadCursor tagged uint64 sign extension regression', () {
+    test('generated cursors match Buffer encodings', () {
       final buffer = Buffer();
-      final testValue = 0x7FFFFFFF;
+      final generated = Buffer();
+
+      buffer.writeBool(true);
+      buffer.writeByte(-7);
+      buffer.writeUint8(250);
+      buffer.writeInt16(-1234);
+      buffer.writeUint16(65000);
+      buffer.writeInt32(-123456789);
+      buffer.writeUint32(0x89abcdef);
+      buffer.writeInt64(-0x1234567890abcdef);
+      buffer.writeUint64(0xfedcba9876543210);
+      buffer.writeFloat16(Float16(1.5));
+      buffer.writeBfloat16(Bfloat16(2.5));
+      buffer.writeFloat32(3.25);
+      buffer.writeFloat64(-9.5);
+      buffer.writeVarUint32(0xffffffff);
+      buffer.writeVarInt32(-0x40000000);
+      buffer.writeVarUint64(0xffffffffffffffff);
+      buffer.writeVarInt64(-0x4000000000000000);
+      buffer.writeTaggedInt64(0x40000000);
+      buffer.writeTaggedUint64(0x80000000);
+
+      final cursor = GeneratedWriteCursor.reserve(generated, 128);
+      cursor.writeBool(true);
+      cursor.writeByte(-7);
+      cursor.writeUint8(250);
+      cursor.writeInt16(-1234);
+      cursor.writeUint16(65000);
+      cursor.writeInt32(-123456789);
+      cursor.writeUint32(0x89abcdef);
+      cursor.writeInt64(-0x1234567890abcdef);
+      cursor.writeUint64(0xfedcba9876543210);
+      cursor.writeFloat16(Float16(1.5));
+      cursor.writeBfloat16(Bfloat16(2.5));
+      cursor.writeFloat32(3.25);
+      cursor.writeFloat64(-9.5);
+      cursor.writeVarUint32(0xffffffff);
+      cursor.writeVarInt32(-0x40000000);
+      cursor.writeVarUint64(0xffffffffffffffff);
+      cursor.writeVarInt64(-0x4000000000000000);
+      cursor.writeTaggedInt64(0x40000000);
+      cursor.writeTaggedUint64(0x80000000);
+      cursor.finish();
+
+      expect(generated.toBytes(), orderedEquals(buffer.toBytes()));
 
-      buffer.writeTaggedUint64(testValue);
-      buffer.wrap(buffer.toBytes());
+      final readBuffer = Buffer.wrap(Uint8List.fromList(generated.toBytes()));
+      final readCursor = GeneratedReadCursor.start(readBuffer);
 
-      final cursor = GeneratedReadCursor.start(buffer);
-      final result = cursor.readTaggedUint64();
+      expect(readCursor.readBool(), isTrue);
+      expect(readCursor.readByte(), equals(-7));
+      expect(readCursor.readUint8(), equals(250));
+      expect(readCursor.readInt16(), equals(-1234));
+      expect(readCursor.readUint16(), equals(65000));
+      expect(readCursor.readInt32(), equals(-123456789));
+      expect(readCursor.readUint32(), equals(0x89abcdef));
+      expect(readCursor.readInt64(), equals(-0x1234567890abcdef));
+      expect(readCursor.readUint64(), equals(0xfedcba9876543210));
+      expect(readCursor.readFloat16(), equals(Float16(1.5)));
+      expect(readCursor.readBfloat16(), equals(Bfloat16(2.5)));
+      expect(readCursor.readFloat32(), closeTo(3.25, 0.0001));
+      expect(readCursor.readFloat64(), equals(-9.5));
+      expect(readCursor.readVarUint32(), equals(0xffffffff));
+      expect(readCursor.readVarInt32(), equals(-0x40000000));
+      expect(readCursor.readVarUint64(), equals(0xffffffffffffffff));
+      expect(readCursor.readVarInt64(), equals(-0x4000000000000000));
+      expect(readCursor.readTaggedInt64(), equals(0x40000000));
+      expect(readCursor.readTaggedUint64(), equals(0x80000000));
 
-      expect(result, equals(testValue));
+      readCursor.finish();
+      expect(readBuffer.readableBytes, equals(0));
     });
   });
 }
+
+void _expectEncodedIntRoundTrip({
+  required int value,
+  required int expectedBytes,
+  required void Function(Buffer buffer, int value) write,
+  required int Function(Buffer buffer) read,
+  int Function(GeneratedReadCursor cursor)? cursorRead,
+}) {
+  final buffer = Buffer();
+  write(buffer, value);
+  final bytes = Uint8List.fromList(buffer.toBytes());
+
+  expect(bytes.length, equals(expectedBytes));
+
+  final wrapped = Buffer.wrap(Uint8List.fromList(bytes));
+  expect(read(wrapped), equals(value));
+  expect(wrapped.readableBytes, equals(0));
+
+  if (cursorRead != null) {
+    final cursorBuffer = Buffer.wrap(Uint8List.fromList(bytes));
+    final cursor = GeneratedReadCursor.start(cursorBuffer);
+    expect(cursorRead(cursor), equals(value));
+    cursor.finish();
+    expect(cursorBuffer.readableBytes, equals(0));
+  }
+}
diff --git a/dart/packages/fory/test/collection_serializer_test.dart b/dart/packages/fory/test/collection_serializer_test.dart
new file mode 100644
index 000000000..4c6fb7739
--- /dev/null
+++ b/dart/packages/fory/test/collection_serializer_test.dart
@@ -0,0 +1,309 @@
+/*
+ * 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.
+ */
+
+import 'dart:convert';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('collection serializer', () {
+    test('round-trips empty root containers', () {
+      final fory = Fory();
+
+      final list =
+          fory.deserialize<Object?>(fory.serialize(<Object?>[])) as List;
+      final set = fory.deserialize<Object?>(fory.serialize(<Object?>{})) as Set;
+      final map = fory
+          .deserialize<Object?>(fory.serialize(<Object?, Object?>{})) as Map;
+
+      expect(list, isA<List>());
+      expect(set, isA<Set>());
+      expect(map, isA<Map>());
+      expect(list, isEmpty);
+      expect(set, isEmpty);
+      expect(map, isEmpty);
+    });
+
+    test('round-trips root list with mixed values and nested containers', () {
+      final fory = Fory();
+      final value = <Object?>[
+        1,
+        true,
+        'alpha',
+        null,
+        <Object?>[1, 2, 3],
+        <Object?, Object?>{
+          'nested': 'map',
+          null: <Object?>[null, 'x'],
+        },
+        <Object?>{'a', null, 'b'},
+      ];
+
+      final roundTrip =
+          fory.deserialize<Object?>(fory.serialize(value)) as List;
+
+      expect(_describeValue(roundTrip), equals(_describeValue(value)));
+      expect(roundTrip[4], isA<List>());
+      expect(roundTrip[5], isA<Map>());
+      expect(roundTrip[6], isA<Set>());
+    });
+
+    test('round-trips root set with mixed values and nested containers', () {
+      final fory = Fory();
+      final value = <Object?>{
+        'alpha',
+        null,
+        1,
+        <Object?>['nested', 1],
+        <Object?, Object?>{'k': 'v'},
+        <Object?>{'x', 'y'},
+      };
+
+      final roundTrip = fory.deserialize<Object?>(fory.serialize(value)) as Set;
+
+      expect(_describeValue(roundTrip), equals(_describeValue(value)));
+    });
+
+    test('round-trips root map with null keys values and nested containers',
+        () {
+      final fory = Fory();
+      final value = <Object?, Object?>{
+        null: 'null-key',
+        'null-value': null,
+        'numbers': <Object?>[1, 2, 3],
+        'nested-map': <Object?, Object?>{
+          'left': 1,
+          'right': <Object?>['x', null, 'y'],
+        },
+        'set': <Object?>{'a', 'b', null},
+      };
+
+      final roundTrip = fory.deserialize<Object?>(fory.serialize(value)) as Map;
+
+      expect(_describeValue(roundTrip), equals(_describeValue(value)));
+      expect(roundTrip.keys.toList(), orderedEquals(value.keys.toList()));
+    });
+
+    test('round-trips nested collection structures', () {
+      final fory = Fory();
+      final value = <Object?, Object?>{
+        'listOfMaps': <Object?>[
+          <Object?, Object?>{
+            'id': 1,
+            'tags': <Object?>{'a', 'b'},
+            'children': <Object?>[1, null, 2],
+          },
+          <Object?, Object?>{
+            'id': 2,
+            'children': <Object?>[
+              <Object?, Object?>{'deep': 'value'},
+            ],
+          },
+        ],
+        'setOfLists': <Object?>{
+          <Object?>[1, 2],
+          <Object?>[3, null, 4],
+        },
+        'mapOfContainers': <Object?, Object?>{
+          'left': <Object?, Object?>{
+            'numbers': <Object?>[1, 2, 3],
+            'flags': <Object?>{true, false},
+          },
+          'right': <Object?, Object?>{
+            'more': <Object?, Object?>{
+              'nested': <Object?>['x', 'y'],
+            },
+          },
+        },
+      };
+
+      final roundTrip = fory.deserialize<Object?>(fory.serialize(value)) as Map;
+
+      expect(_describeValue(roundTrip), equals(_describeValue(value)));
+      expect(roundTrip['listOfMaps'], isA<List>());
+      expect(roundTrip['setOfLists'], isA<Set>());
+      expect(roundTrip['mapOfContainers'], isA<Map>());
+    });
+
+    test('trackRef false does not preserve repeated nested collections', () {
+      final fory = Fory();
+      final sharedList = <Object?>['shared'];
+      final sharedMap = <Object?, Object?>{'value': 1};
+
+      final listRoundTrip = fory.deserialize<Object?>(
+          fory.serialize(<Object?>[sharedList, sharedList])) as List;
+      final mapRoundTrip = fory.deserialize<Object?>(
+        fory.serialize(<Object?, Object?>{'a': sharedMap, 'b': sharedMap}),
+      ) as Map;
+
+      expect(
+          _describeValue(listRoundTrip),
+          equals(_describeValue(<Object?>[
+            sharedList,
+            sharedList,
+          ])));
+      expect(identical(listRoundTrip[0], listRoundTrip[1]), isFalse);
+      expect(identical(mapRoundTrip['a'], mapRoundTrip['b']), isFalse);
+    });
+
+    test('trackRef true preserves repeated nested collections', () {
+      final fory = Fory();
+      final sharedList = <Object?>['shared'];
+      final sharedMap = <Object?, Object?>{'value': 1};
+      final nested = <Object?, Object?>{
+        'list': <Object?>[sharedList, sharedList],
+        'map': <Object?, Object?>{'a': sharedMap, 'b': sharedMap},
+      };
+
+      final roundTrip = fory.deserialize<Object?>(
+        fory.serialize(nested, trackRef: true),
+      ) as Map;
+      final listRoundTrip = roundTrip['list'] as List;
+      final mapRoundTrip = roundTrip['map'] as Map;
+
+      expect(_describeValue(roundTrip), equals(_describeValue(nested)));
+      expect(identical(listRoundTrip[0], listRoundTrip[1]), isTrue);
+      expect(identical(mapRoundTrip['a'], mapRoundTrip['b']), isTrue);
+    });
+
+    test('trackRef true preserves self-referential root lists', () {
+      final fory = Fory();
+      final value = <Object?>[];
+      value.add(value);
+
+      final roundTrip = fory.deserialize<Object?>(
+        fory.serialize(value, trackRef: true),
+      ) as List;
+
+      expect(roundTrip, hasLength(1));
+      expect(identical(roundTrip, roundTrip[0]), isTrue);
+    });
+
+    test('trackRef true preserves self-referential root maps', () {
+      final fory = Fory();
+      final value = <Object?, Object?>{};
+      value['self'] = value;
+
+      final roundTrip = fory.deserialize<Object?>(
+        fory.serialize(value, trackRef: true),
+      ) as Map;
+
+      expect(roundTrip.keys, contains('self'));
+      expect(identical(roundTrip, roundTrip['self']), isTrue);
+    });
+
+    test('round-trips large list, set, and map payloads', () {
+      final fory = Fory();
+      final largeList = List<Object?>.generate(600, (index) => 'item-$index',
+          growable: false);
+      final largeSet = Set<Object?>.of(largeList);
+      final largeMap = <Object?, Object?>{
+        for (var index = 0; index < 320; index += 1) 'k$index': index,
+      };
+
+      final listRoundTrip =
+          fory.deserialize<Object?>(fory.serialize(largeList)) as List;
+      final setRoundTrip =
+          fory.deserialize<Object?>(fory.serialize(largeSet)) as Set;
+      final mapRoundTrip =
+          fory.deserialize<Object?>(fory.serialize(largeMap)) as Map;
+
+      expect(_describeValue(listRoundTrip), equals(_describeValue(largeList)));
+      expect(_describeValue(setRoundTrip), equals(_describeValue(largeSet)));
+      expect(_describeValue(mapRoundTrip), equals(_describeValue(largeMap)));
+      expect(mapRoundTrip.keys.toList(), orderedEquals(largeMap.keys.toList()));
+    });
+
+    test(
+        'reuses the same fory instance and target buffer across collection shapes',
+        () {
+      final fory = Fory();
+      final buffer = Buffer();
+      final cases = <Object?>[
+        <Object?>[1, 2, 3],
+        <Object?>{'a', null, 'b'},
+        <Object?, Object?>{
+          'nested': <Object?>[
+            <Object?, Object?>{'x': 1},
+            null,
+            <Object?>{'y', 'z'},
+          ],
+        },
+        <Object?, Object?>{
+          'shared': <Object?>[
+            <Object?>['x'],
+            <Object?>['x'],
+          ],
+        },
+      ];
+
+      for (final value in cases) {
+        fory.serializeTo(value, buffer);
+        final roundTrip = fory.deserializeFrom<Object?>(buffer);
+        expect(_describeValue(roundTrip), equals(_describeValue(value)));
+        expect(buffer.readableBytes, equals(0));
+      }
+    });
+
+    test('enforces maxCollectionSize for list set and map', () {
+      final fory = Fory(maxCollectionSize: 2);
+
+      expect(
+        () => fory.serialize(<Object?>[1, 2, 3]),
+        throwsA(isA<StateError>()),
+      );
+      expect(
+        () => fory.serialize(<Object?>{1, 2, 3}),
+        throwsA(isA<StateError>()),
+      );
+      expect(
+        () => fory.serialize(<Object?, Object?>{'a': 1, 'b': 2, 'c': 3}),
+        throwsA(isA<StateError>()),
+      );
+    });
+  });
+}
+
+String _describeValue(Object? value) {
+  if (value == null) {
+    return 'null';
+  }
+  if (value is String) {
+    return jsonEncode(value);
+  }
+  if (value is bool || value is num) {
+    return value.toString();
+  }
+  if (value is List) {
+    return '[${value.map(_describeValue).join(',')}]';
+  }
+  if (value is Set) {
+    final items = value.map(_describeValue).toList()..sort();
+    return 'set(${items.join(',')})';
+  }
+  if (value is Map) {
+    final entries = value.entries
+        .map((entry) =>
+            '${_describeValue(entry.key)}:${_describeValue(entry.value)}')
+        .join(',');
+    return 'map($entries)';
+  }
+  return jsonEncode(value.toString());
+}
diff --git a/dart/packages/fory/test/enum_union_serializer_test.dart b/dart/packages/fory/test/enum_union_serializer_test.dart
new file mode 100644
index 000000000..1b65d85f0
--- /dev/null
+++ b/dart/packages/fory/test/enum_union_serializer_test.dart
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+part 'enum_union_serializer_test.fory.dart';
+
+@ForyStruct()
+enum SimpleColor {
+  red,
+  green,
+  blue,
+}
+
+@ForyStruct()
+enum StableCodeV1 {
+  alpha(7),
+  beta(11);
+
+  const StableCodeV1(this.rawValue);
+
+  final int rawValue;
+
+  static StableCodeV1 fromRawValue(int rawValue) {
+    return switch (rawValue) {
+      7 => StableCodeV1.alpha,
+      11 => StableCodeV1.beta,
+      _ => throw StateError('Unknown StableCodeV1 raw value $rawValue.'),
+    };
+  }
+}
+
+@ForyStruct()
+enum StableCodeV2 {
+  betaRenamed(11),
+  alphaRenamed(7);
+
+  const StableCodeV2(this.rawValue);
+
+  final int rawValue;
+
+  static StableCodeV2 fromRawValue(int rawValue) {
+    return switch (rawValue) {
+      7 => StableCodeV2.alphaRenamed,
+      11 => StableCodeV2.betaRenamed,
+      _ => throw StateError('Unknown StableCodeV2 raw value $rawValue.'),
+    };
+  }
+}
+
+@ForyStruct()
+class EnumEnvelope {
+  EnumEnvelope();
+
+  SimpleColor color = SimpleColor.red;
+}
+
+@ForyStruct()
+class UnionLeaf {
+  UnionLeaf();
+
+  String label = '';
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) || other is UnionLeaf && other.label == label;
+
+  @override
+  int get hashCode => label.hashCode;
+}
+
+@ForyUnion()
+final class TestUnion {
+  const TestUnion._(this.index, this.value);
+
+  final int index;
+  final Object value;
+
+  factory TestUnion.ofString(String value) => TestUnion._(0, value);
+
+  factory TestUnion.ofInt(int value) => TestUnion._(1, value);
+
+  factory TestUnion.ofLeaf(UnionLeaf value) => TestUnion._(2, value);
+
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      other is TestUnion && other.index == index && other.value == value;
+
+  @override
+  int get hashCode => Object.hash(index, value);
+}
+
+final class TestUnionSerializer extends UnionSerializer<TestUnion> {
+  const TestUnionSerializer();
+
+  @override
+  int caseId(TestUnion value) => value.index;
+
+  @override
+  Object caseValue(TestUnion value) => value.value;
+
+  @override
+  TestUnion buildValue(int index, Object? value) {
+    if (index == 0 && value is String) {
+      return TestUnion.ofString(value);
+    }
+    if (index == 1 && value is int) {
+      return TestUnion.ofInt(value);
+    }
+    if (index == 2 && value is UnionLeaf) {
+      return TestUnion.ofLeaf(value);
+    }
+    throw StateError('Unsupported TestUnion case $index with value $value.');
+  }
+}
+
+@ForyStruct()
+class UnionEnvelope {
+  UnionEnvelope();
+
+  TestUnion? payload;
+}
+
+void _registerEnumAndUnionTypes(Fory fory) {
+  EnumUnionSerializerTestFory.register(
+    fory,
+    SimpleColor,
+    namespace: 'test',
+    typeName: 'SimpleColor',
+  );
+  EnumUnionSerializerTestFory.register(
+    fory,
+    EnumEnvelope,
+    namespace: 'test',
+    typeName: 'EnumEnvelope',
+  );
+  EnumUnionSerializerTestFory.register(
+    fory,
+    UnionLeaf,
+    namespace: 'test',
+    typeName: 'UnionLeaf',
+  );
+  EnumUnionSerializerTestFory.register(
+    fory,
+    UnionEnvelope,
+    namespace: 'test',
+    typeName: 'UnionEnvelope',
+  );
+  fory.registerSerializer(
+    TestUnion,
+    const TestUnionSerializer(),
+    namespace: 'test',
+    typeName: 'TestUnion',
+  );
+}
+
+void _registerRawEnumV1(Fory fory) {
+  EnumUnionSerializerTestFory.register(
+    fory,
+    StableCodeV1,
+    namespace: 'enum',
+    typeName: 'StableCode',
+  );
+}
+
+void _registerRawEnumV2(Fory fory) {
+  EnumUnionSerializerTestFory.register(
+    fory,
+    StableCodeV2,
+    namespace: 'enum',
+    typeName: 'StableCode',
+  );
+}
+
+void main() {
+  group('enum serializer', () {
+    test('round-trips root enums and generated enum fields', () {
+      final fory = Fory();
+      _registerEnumAndUnionTypes(fory);
+
+      final rootRoundTrip =
+          fory.deserialize<SimpleColor>(fory.serialize(SimpleColor.blue));
+      final envelopeRoundTrip = fory.deserialize<EnumEnvelope>(
+        fory.serialize(EnumEnvelope()..color = SimpleColor.green),
+      );
+
+      expect(rootRoundTrip, equals(SimpleColor.blue));
+      expect(envelopeRoundTrip.color, equals(SimpleColor.green));
+    });
+
+    test('uses raw enum values across reorder and rename', () {
+      final writer = Fory();
+      final reader = Fory();
+      _registerRawEnumV1(writer);
+      _registerRawEnumV2(reader);
+
+      expect(
+        reader.deserialize<StableCodeV2>(writer.serialize(StableCodeV1.alpha)),
+        equals(StableCodeV2.alphaRenamed),
+      );
+      expect(
+        reader.deserialize<StableCodeV2>(writer.serialize(StableCodeV1.beta)),
+        equals(StableCodeV2.betaRenamed),
+      );
+    });
+  });
+
+  group('union serializer', () {
+    test('round-trips manual union roots for every arm', () {
+      final fory = Fory();
+      _registerEnumAndUnionTypes(fory);
+
+      final cases = <TestUnion>[
+        TestUnion.ofString('alpha'),
+        TestUnion.ofInt(1234),
+        TestUnion.ofLeaf(UnionLeaf()..label = 'leaf'),
+      ];
+
+      for (final value in cases) {
+        final roundTrip = fory.deserialize<TestUnion>(fory.serialize(value));
+        expect(roundTrip, equals(value));
+      }
+    });
+
+    test('round-trips nullable union fields in schema-consistent mode', () {
+      final fory = Fory();
+      _registerEnumAndUnionTypes(fory);
+
+      final leafEnvelope = UnionEnvelope()
+        ..payload = TestUnion.ofLeaf(UnionLeaf()..label = 'branch');
+      final nullEnvelope = UnionEnvelope();
+
+      final leafRoundTrip = fory.deserialize<UnionEnvelope>(
+        fory.serialize(leafEnvelope),
+      );
+      final nullRoundTrip = fory.deserialize<UnionEnvelope>(
+        fory.serialize(nullEnvelope),
+      );
+
+      expect(leafRoundTrip.payload, equals(leafEnvelope.payload));
+      expect(nullRoundTrip.payload, isNull);
+    });
+
+    test('round-trips union fields in compatible mode', () {
+      final fory = Fory(compatible: true);
+      _registerEnumAndUnionTypes(fory);
+
+      final roundTrip = fory.deserialize<UnionEnvelope>(
+        fory.serialize(
+          UnionEnvelope()..payload = TestUnion.ofString('compatible'),
+        ),
+      );
+
+      expect(roundTrip.payload, equals(TestUnion.ofString('compatible')));
+    });
+  });
+}
diff --git a/dart/packages/fory/test/numeric_wrapper_test.dart b/dart/packages/fory/test/numeric_wrapper_test.dart
new file mode 100644
index 000000000..a944761f8
--- /dev/null
+++ b/dart/packages/fory/test/numeric_wrapper_test.dart
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+part 'numeric_wrapper_test.fory.dart';
+
+@ForyStruct()
+class NumericWrappersEnvelope {
+  NumericWrappersEnvelope();
+
+  Int8 i8 = Int8(0);
+  Int16 i16 = Int16(0);
+  Int32 i32 = Int32(0);
+  Uint8 u8 = Uint8(0);
+  Uint16 u16 = Uint16(0);
+  Uint32 u32 = Uint32(0);
+  Uint64 u64 = Uint64(0);
+  Float16 half = Float16(0);
+  Bfloat16 brain = Bfloat16(0);
+  Float32 single = Float32(0);
+  Int8? optionalI8;
+  Uint64? optionalU64;
+  Float16? optionalHalf;
+  Bfloat16? optionalBrain;
+  Float32? optionalSingle;
+}
+
+void _registerNumericWrappers(Fory fory) {
+  NumericWrapperTestFory.register(
+    fory,
+    NumericWrappersEnvelope,
+    namespace: 'test',
+    typeName: 'NumericWrappersEnvelope',
+  );
+}
+
+T _roundTrip<T>(Fory fory, T value) =>
+    fory.deserialize<T>(fory.serialize(value));
+
+NumericWrappersEnvelope _sampleEnvelope() {
+  return NumericWrappersEnvelope()
+    ..i8 = Int8(-127)
+    ..i16 = Int16(0x7fff)
+    ..i32 = Int32(-2147483648)
+    ..u8 = Uint8(0xff)
+    ..u16 = Uint16(0xffff)
+    ..u32 = Uint32(0xffffffff)
+    ..u64 = Uint64(0xffffffffffffffff)
+    ..half = Float16.fromBits(0x3555)
+    ..brain = Bfloat16.fromBits(0x3eab)
+    ..single = Float32.fromBits(0x40490fdb)
+    ..optionalI8 = Int8(126)
+    ..optionalU64 = Uint64(0x8000000000000000)
+    ..optionalHalf = Float16.fromBits(0x8000)
+    ..optionalBrain = Bfloat16.fromBits(0x8000)
+    ..optionalSingle = Float32.fromBits(0x80000000);
+}
+
+void _expectEnvelopeEquals(
+  NumericWrappersEnvelope actual,
+  NumericWrappersEnvelope expected,
+) {
+  expect(actual.i8, equals(expected.i8));
+  expect(actual.i16, equals(expected.i16));
+  expect(actual.i32, equals(expected.i32));
+  expect(actual.u8, equals(expected.u8));
+  expect(actual.u16, equals(expected.u16));
+  expect(actual.u32, equals(expected.u32));
+  expect(actual.u64, equals(expected.u64));
+  expect(actual.half.toBits(), equals(expected.half.toBits()));
+  expect(actual.brain.toBits(), equals(expected.brain.toBits()));
+  expect(actual.single.toBits(), equals(expected.single.toBits()));
+  expect(actual.optionalI8, equals(expected.optionalI8));
+  expect(actual.optionalU64, equals(expected.optionalU64));
+  expect(
+      actual.optionalHalf?.toBits(), equals(expected.optionalHalf?.toBits()));
+  expect(
+      actual.optionalBrain?.toBits(), equals(expected.optionalBrain?.toBits()));
+  expect(
+    actual.optionalSingle?.toBits(),
+    equals(expected.optionalSingle?.toBits()),
+  );
+}
+
+void main() {
+  group('numeric wrappers', () {
+    test('signed integer wrappers normalize arithmetic and bitwise operations',
+        () {
+      expect(Int8(127) + 1, equals(Int8(-128)));
+      expect(Int8(-128) - 1, equals(Int8(127)));
+      expect(Int8(12) * Int8(11), equals(Int8(-124)));
+      expect(Int8(7) ~/ 2, equals(Int8(3)));
+      expect(Int8(7) / 2, equals(3.5));
+      expect(Int8(0x0f) & Int8(0x33), equals(Int8(0x03)));
+      expect(Int8(0x0f) | Int8(0x33), equals(Int8(0x3f)));
+      expect(Int8(0x0f) ^ Int8(0x33), equals(Int8(0x3c)));
+      expect(Int8(0x40) >> 1, equals(Int8(0x20)));
+      expect(Int8(0x40) << 2, equals(Int8(0x00)));
+      expect(Int8(-1) < 0, isTrue);
+      expect(Int8(10) >= Int8(10), isTrue);
+
+      expect(Int16(0x7fff) + 1, equals(Int16(-0x8000)));
+      expect(Int16(-0x8000) - 1, equals(Int16(0x7fff)));
+      expect(Int32(0x7fffffff) + 1, equals(Int32(-0x80000000)));
+      expect(Int32(-0x80000000) - 1, equals(Int32(0x7fffffff)));
+      expect(Int32(0x12345678) >> 4, equals(Int32(0x01234567)));
+      expect(Int32(123).toInt(), equals(123));
+    });
+
+    test(
+        'unsigned integer wrappers normalize arithmetic and bitwise operations',
+        () {
+      expect(Uint8(0xff) + 1, equals(Uint8(0)));
+      expect(Uint8(0) - 1, equals(Uint8(0xff)));
+      expect(Uint8(0xf0) >> 4, equals(Uint8(0x0f)));
+      expect(-Uint8(1), equals(Uint8(0xff)));
+
+      expect(Uint16(0xffff) + 1, equals(Uint16(0)));
+      expect(Uint16(0) - 1, equals(Uint16(0xffff)));
+
+      expect(Uint32(0xffffffff) + 1, equals(Uint32(0)));
+      expect(Uint32(0) - 1, equals(Uint32(0xffffffff)));
+      expect(
+          Uint32(0xf0f0f0f0) & Uint32(0x0ff00ff0), equals(Uint32(0x00f000f0)));
+
+      expect(Uint64(0xffffffffffffffff) + 1, equals(Uint64(0)));
+      expect(Uint64(0) - 1, equals(Uint64(0xffffffffffffffff)));
+      expect(
+          Uint64(0x123456789abcdef0) >> 4, equals(Uint64(0x0123456789abcdef)));
+      expect(Uint64(0xff).toInt(), equals(0xff));
+    });
+
+    test('Float16 arithmetic rounds back to binary16 precision', () {
+      expect((Float16(1.5) + 2).toBits(), equals(Float16(3.5).toBits()));
+      expect(
+          (Float16(7) - Float16(2.5)).toBits(), equals(Float16(4.5).toBits()));
+      expect((Float16(1) / 3).toBits(), equals(Float16(1 / 3).toBits()));
+      expect((Float16(7.5) % 2).toBits(), equals(Float16(1.5).toBits()));
+      expect(Float16(7.5) ~/ 2, equals(3));
+      expect((-Float16(1.5)).toBits(), equals(Float16(-1.5).toBits()));
+      expect(Float16(-1) < 0, isTrue);
+      expect(Float16(3.5) >= Float16(3.5), isTrue);
+      expect(Float16.fromBits(0x3555).value, closeTo(0.333251953125, 1e-12));
+    });
+
+    test('Bfloat16 arithmetic rounds back to bfloat16 precision', () {
+      expect((Bfloat16(1.5) + 2).toBits(), equals(Bfloat16(3.5).toBits()));
+      expect(
+        (Bfloat16(7) - Bfloat16(2.5)).toBits(),
+        equals(Bfloat16(4.5).toBits()),
+      );
+      expect((Bfloat16(1) / 3).toBits(), equals(Bfloat16(1 / 3).toBits()));
+      expect((Bfloat16(7.5) % 2).toBits(), equals(Bfloat16(1.5).toBits()));
+      expect(Bfloat16(7.5) ~/ 2, equals(3));
+      expect((-Bfloat16(1.5)).toBits(), equals(Bfloat16(-1.5).toBits()));
+      expect(Bfloat16(-1) < 0, isTrue);
+      expect(Bfloat16(3.5) >= Bfloat16(3.5), isTrue);
+      expect(Bfloat16.fromBits(0x3eab).value, closeTo(0.333984375, 1e-12));
+    });
+
+    test(
+        'Bfloat16.fromDouble rounds directly from float64 and preserves NaN sign payload bits',
+        () {
+      final subnormalSource = ByteData(8)
+        ..setUint64(0, 0x37da834f7e281cc1, Endian.little);
+      final trickySubnormal = subnormalSource.getFloat64(0, Endian.little);
+      final nanSource = ByteData(8)
+        ..setUint64(0, 0xfff123456789abcd, Endian.little);
+      final payloadNaN = nanSource.getFloat64(0, Endian.little);
+      final convertedNaN = Bfloat16.fromDouble(payloadNaN);
+
+      expect(Bfloat16.fromDouble(trickySubnormal).toBits(), equals(0x0007));
+      expect(convertedNaN.toBits() & 0x8000, equals(0x8000));
+      expect(convertedNaN.toBits() & 0x7f80, equals(0x7f80));
+      expect(convertedNaN.toBits() & 0x0040, equals(0x0040));
+      expect(convertedNaN.toBits() & 0x003f, isNot(0));
+    });
+
+    test('Float32 arithmetic rounds back to binary32 precision', () {
+      expect((Float32(1.5) + 2).toBits(), equals(Float32(3.5).toBits()));
+      expect(
+          (Float32(7) - Float32(2.5)).toBits(), equals(Float32(4.5).toBits()));
+      expect((Float32(1) / 3).toBits(), equals(Float32(1 / 3).toBits()));
+      expect((Float32(7.5) % 2).toBits(), equals(Float32(1.5).toBits()));
+      expect(Float32(7.5) ~/ 2, equals(3));
+      expect((-Float32(1.5)).toBits(), equals(Float32(-1.5).toBits()));
+      expect(Float32(-1) < 0, isTrue);
+      expect(Float32(3.5) >= Float32(3.5), isTrue);
+      expect(Float32.fromBits(0x3eaaaaab).value, closeTo(1 / 3, 1e-8));
+    });
+
+    test('round-trips root fixed-width wrappers', () {
+      final fory = Fory();
+
+      expect(_roundTrip<Int8>(fory, Int8(-129)), equals(Int8(127)));
+      expect(_roundTrip<Int16>(fory, Int16(-32769)), equals(Int16(32767)));
+      expect(
+        _roundTrip<Int32>(fory, Int32(0x80000000)),
+        equals(Int32(-0x80000000)),
+      );
+      expect(_roundTrip<Uint8>(fory, Uint8(-1)), equals(Uint8(0xff)));
+      expect(_roundTrip<Uint16>(fory, Uint16(-1)), equals(Uint16(0xffff)));
+      expect(_roundTrip<Uint32>(fory, Uint32(-1)), equals(Uint32(0xffffffff)));
+      expect(
+        _roundTrip<Uint64>(fory, Uint64(-1)),
+        equals(Uint64(0xffffffffffffffff)),
+      );
+    });
+
+    test('round-trips root Float16 payloads with exact bits', () {
+      final fory = Fory();
+      final cases = <Float16>[
+        Float16.fromBits(0x0000),
+        Float16.fromBits(0x8000),
+        Float16.fromBits(0x3555),
+        Float16.fromBits(0x7c00),
+        Float16.fromBits(0x7e00),
+      ];
+
+      for (final value in cases) {
+        expect(
+            _roundTrip<Float16>(fory, value).toBits(), equals(value.toBits()));
+      }
+    });
+
+    test('round-trips root Bfloat16 payloads with exact bits', () {
+      final fory = Fory();
+      final cases = <Bfloat16>[
+        Bfloat16.fromBits(0x0000),
+        Bfloat16.fromBits(0x8000),
+        Bfloat16.fromBits(0x3eab),
+        Bfloat16.fromBits(0x7f80),
+        Bfloat16.fromBits(0x7fc0),
+      ];
+
+      for (final value in cases) {
+        expect(
+          _roundTrip<Bfloat16>(fory, value).toBits(),
+          equals(value.toBits()),
+        );
+      }
+    });
+
+    test('round-trips root Float32 payloads with exact bits', () {
+      final fory = Fory();
+      final cases = <Float32>[
+        Float32.fromBits(0x00000000),
+        Float32.fromBits(0x80000000),
+        Float32.fromBits(0x3eaaaaab),
+        Float32.fromBits(0x7f800000),
+        Float32.fromBits(0x7fc00000),
+      ];
+
+      for (final value in cases) {
+        expect(
+            _roundTrip<Float32>(fory, value).toBits(), equals(value.toBits()));
+      }
+    });
+
+    test(
+        'round-trips generated numeric wrapper fields in schema-consistent mode',
+        () {
+      final fory = Fory();
+      _registerNumericWrappers(fory);
+
+      final value = _sampleEnvelope();
+      final roundTrip =
+          fory.deserialize<NumericWrappersEnvelope>(fory.serialize(value));
+
+      _expectEnvelopeEquals(roundTrip, value);
+    });
+
+    test('round-trips generated numeric wrapper fields in compatible mode', () {
+      final fory = Fory(compatible: true);
+      _registerNumericWrappers(fory);
+
+      final value = _sampleEnvelope();
+      final roundTrip =
+          fory.deserialize<NumericWrappersEnvelope>(fory.serialize(value));
+
+      _expectEnvelopeEquals(roundTrip, value);
+    });
+
+    test('supports null optional numeric wrapper fields', () {
+      final fory = Fory();
+      _registerNumericWrappers(fory);
+
+      final roundTrip = _roundTrip<NumericWrappersEnvelope>(
+        fory,
+        NumericWrappersEnvelope(),
+      );
+
+      expect(roundTrip.optionalI8, isNull);
+      expect(roundTrip.optionalU64, isNull);
+      expect(roundTrip.optionalHalf, isNull);
+      expect(roundTrip.optionalBrain, isNull);
+      expect(roundTrip.optionalSingle, isNull);
+    });
+  });
+}
diff --git a/dart/packages/fory/test/object_and_compatible_serializer_test.dart b/dart/packages/fory/test/object_and_compatible_serializer_test.dart
new file mode 100644
index 000000000..73174d35a
--- /dev/null
+++ b/dart/packages/fory/test/object_and_compatible_serializer_test.dart
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+import 'package:fory/fory.dart';
+import 'package:fory/src/serializer/compatible_struct_metadata.dart';
+import 'package:test/test.dart';
+
+part 'object_and_compatible_serializer_test.fory.dart';
+
+@ForyStruct()
+class SharedLeaf {
+  SharedLeaf();
+
+  String label = '';
+}
+
+@ForyStruct()
+class RefPair {
+  RefPair();
+
+  @ForyField(ref: true)
+  SharedLeaf? first;
+
+  @ForyField(ref: true)
+  SharedLeaf? second;
+}
+
+@ForyStruct()
+class NoRefPair {
+  NoRefPair();
+
+  SharedLeaf? first;
+  SharedLeaf? second;
+}
+
+@ForyStruct()
+class CircularNode {
+  CircularNode();
+
+  String name = '';
+
+  @ForyField(ref: true)
+  CircularNode? next;
+}
+
+@ForyStruct()
+class ImmutableRefPair {
+  const ImmutableRefPair(this.first, this.second);
+
+  @ForyField(ref: true)
+  final SharedLeaf? first;
+
+  @ForyField(ref: true)
+  final SharedLeaf? second;
+}
+
+@ForyStruct()
+class CompatibleEnvelopeV1 {
+  CompatibleEnvelopeV1();
+
+  @ForyField(id: 1)
+  String name = '';
+
+  @ForyField(id: 2)
+  int age = 0;
+
+  @ForyField(id: 3, dynamic: true)
+  Object? payload;
+
+  @ForyField(id: 4, ref: true)
+  SharedLeaf? first;
+
+  @ForyField(id: 5, ref: true)
+  SharedLeaf? second;
+}
+
+@ForyStruct()
+class CompatibleEnvelopeV2 {
+  CompatibleEnvelopeV2();
+
+  @ForyField(id: 5, ref: true)
+  SharedLeaf? duplicate;
+
+  @ForyField(id: 1)
+  String displayName = '';
+
+  @ForyField(id: 6)
+  String city = 'unknown';
+
+  @ForyField(id: 4, ref: true)
+  SharedLeaf? original;
+
+  @ForyField(id: 3, dynamic: true)
+  Object? payload;
+}
+
+void _registerCommonTypes(Fory fory) {
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    SharedLeaf,
+    namespace: 'test',
+    typeName: 'SharedLeaf',
+  );
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    RefPair,
+    namespace: 'test',
+    typeName: 'RefPair',
+  );
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    NoRefPair,
+    namespace: 'test',
+    typeName: 'NoRefPair',
+  );
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    CircularNode,
+    namespace: 'test',
+    typeName: 'CircularNode',
+  );
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    ImmutableRefPair,
+    namespace: 'test',
+    typeName: 'ImmutableRefPair',
+  );
+}
+
+void _registerV1Types(Fory fory) {
+  _registerCommonTypes(fory);
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    CompatibleEnvelopeV1,
+    namespace: 'compat',
+    typeName: 'Envelope',
+  );
+}
+
+void _registerV2Types(Fory fory) {
+  _registerCommonTypes(fory);
+  ObjectAndCompatibleSerializerTestFory.register(
+    fory,
+    CompatibleEnvelopeV2,
+    namespace: 'compat',
+    typeName: 'Envelope',
+  );
+}
+
+void main() {
+  group('generated struct serialization', () {
+    test('direct ref fields preserve identity while plain fields do not', () {
+      final fory = Fory();
+      _registerCommonTypes(fory);
+
+      final shared = SharedLeaf()..label = 'shared';
+      final refRoundTrip = fory.deserialize<RefPair>(
+        fory.serialize(
+          RefPair()
+            ..first = shared
+            ..second = shared,
+        ),
+      );
+      final noRefRoundTrip = fory.deserialize<NoRefPair>(
+        fory.serialize(
+          NoRefPair()
+            ..first = shared
+            ..second = shared,
+        ),
+      );
+
+      expect(refRoundTrip.first!.label, equals('shared'));
+      expect(identical(refRoundTrip.first, refRoundTrip.second), isTrue);
+      expect(noRefRoundTrip.first!.label, equals('shared'));
+      expect(identical(noRefRoundTrip.first, noRefRoundTrip.second), isFalse);
+    });
+
+    test('constructor-based generated structs preserve shared references', () {
+      final fory = Fory();
+      _registerCommonTypes(fory);
+
+      final shared = SharedLeaf()..label = 'immutable';
+      final roundTrip = fory.deserialize<ImmutableRefPair>(
+        fory.serialize(ImmutableRefPair(shared, shared)),
+      );
+
+      expect(roundTrip.first!.label, equals('immutable'));
+      expect(identical(roundTrip.first, roundTrip.second), isTrue);
+    });
+
+    test('mutable generated structs round trip self references', () {
+      final fory = Fory();
+      _registerCommonTypes(fory);
+
+      final node = CircularNode()..name = 'loop';
+      node.next = node;
+
+      final roundTrip = fory.deserialize<CircularNode>(fory.serialize(node));
+
+      expect(roundTrip.name, equals('loop'));
+      expect(identical(roundTrip, roundTrip.next), isTrue);
+    });
+  });
+
+  group('compatible generated structs', () {
+    test(
+        'match fields by stable ids across rename, reorder, and missing fields',
+        () {
+      final writer = Fory(compatible: true);
+      final reader = Fory(compatible: true);
+      _registerV1Types(writer);
+      _registerV2Types(reader);
+
+      final shared = SharedLeaf()..label = 'shared';
+      final migrated = reader.deserialize<CompatibleEnvelopeV2>(
+        writer.serialize(
+          CompatibleEnvelopeV1()
+            ..name = 'Ada'
+            ..age = 36
+            ..payload = shared
+            ..first = shared
+            ..second = shared,
+        ),
+      );
+
+      expect(migrated.displayName, equals('Ada'));
+      expect(migrated.city, equals('unknown'));
+      expect(migrated.payload, isA<SharedLeaf>());
+      expect((migrated.payload as SharedLeaf).label, equals('shared'));
+      expect(identical(migrated.original, migrated.duplicate), isTrue);
+
+      final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(migrated);
+      expect(remoteTypeDef, isNotNull);
+      expect(
+        remoteTypeDef!.fields.map((field) => field.identifier),
+        containsAll(<String>['1', '2', '3', '4', '5']),
+      );
+
+      final roundTripBack = writer.deserialize<CompatibleEnvelopeV1>(
+        reader.serialize(migrated),
+      );
+
+      expect(roundTripBack.name, equals('Ada'));
+      expect(roundTripBack.age, equals(0));
+      expect(roundTripBack.payload, isA<SharedLeaf>());
+      expect((roundTripBack.payload as SharedLeaf).label, equals('shared'));
+      expect(identical(roundTripBack.first, roundTripBack.second), isTrue);
+    });
+
+    test('reserializes compatible structs with local TypeDef ordering', () {
+      final writer = Fory(compatible: true);
+      final reader = Fory(compatible: true);
+      _registerV1Types(writer);
+      _registerV2Types(reader);
+
+      final migratedShared = SharedLeaf()..label = 'shared';
+      final migrated = reader.deserialize<CompatibleEnvelopeV2>(
+        writer.serialize(
+          CompatibleEnvelopeV1()
+            ..name = 'Ada'
+            ..age = 36
+            ..payload = migratedShared
+            ..first = migratedShared
+            ..second = migratedShared,
+        ),
+      );
+
+      final localShared = SharedLeaf()..label = 'shared';
+      final local = CompatibleEnvelopeV2()
+        ..displayName = 'Ada'
+        ..city = 'unknown'
+        ..payload = localShared
+        ..original = localShared
+        ..duplicate = localShared;
+
+      expect(
+        reader.serialize(migrated),
+        orderedEquals(reader.serialize(local)),
+      );
+    });
+  });
+}
diff --git a/dart/packages/fory/test/runtime_validation_test.dart b/dart/packages/fory/test/runtime_validation_test.dart
new file mode 100644
index 000000000..4e1c39509
--- /dev/null
+++ b/dart/packages/fory/test/runtime_validation_test.dart
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ */
+
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+part 'runtime_validation_test.fory.dart';
+
+final class PlainManualValue {
+  PlainManualValue(this.value);
+
+  final String value;
+}
+
+final class PlainManualValueSerializer extends Serializer<PlainManualValue> {
+  const PlainManualValueSerializer();
+
+  @override
+  void write(WriteContext context, PlainManualValue value) {
+    context.writeString(value.value);
+  }
+
+  @override
+  PlainManualValue read(ReadContext context) {
+    return PlainManualValue(context.readString());
+  }
+}
+
+@ForyStruct()
+class FreshGeneratedValue {
+  FreshGeneratedValue();
+
+  String value = '';
+}
+
+abstract interface class DynamicAnimal {
+  String get name;
+}
+
+@ForyStruct()
+class DynamicDog implements DynamicAnimal {
+  DynamicDog();
+
+  @override
+  String name = '';
+}
+
+@ForyStruct()
+class DynamicCat implements DynamicAnimal {
+  DynamicCat();
+
+  @override
+  String name = '';
+
+  int lives = 9;
+}
+
+@ForyStruct()
+class DynamicAnimalEnvelope {
+  DynamicAnimalEnvelope();
+
+  @ForyField(dynamic: true)
+  DynamicAnimal? animal;
+}
+
+@ForyStruct()
+class SkipEnvelope {
+  SkipEnvelope();
+
+  String visible = '';
+
+  @ForyField(skip: true)
+  String skipped = 'local-default';
+}
+
+@ForyStruct()
+class SkipCompatibleV1 {
+  SkipCompatibleV1();
+
+  @ForyField(id: 1)
+  String visible = '';
+
+  @ForyField(id: 2)
+  String skipped = 'writer-default';
+}
+
+@ForyStruct()
+class SkipCompatibleV2 {
+  SkipCompatibleV2();
+
+  @ForyField(id: 1)
+  String visible = '';
+
+  @ForyField(id: 2, skip: true)
+  String skipped = 'reader-default';
+}
+
+@ForyStruct()
+class SchemaVersionV1 {
+  SchemaVersionV1();
+
+  String label = '';
+}
+
+@ForyStruct()
+class SchemaVersionV2 {
+  SchemaVersionV2();
+
+  String label = '';
+  int count = 0;
+}
+
+void _registerValidationTypes(Fory fory) {
+  RuntimeValidationTestFory.register(
+    fory,
+    DynamicDog,
+    namespace: 'validation',
+    typeName: 'DynamicDog',
+  );
+  RuntimeValidationTestFory.register(
+    fory,
+    DynamicCat,
+    namespace: 'validation',
+    typeName: 'DynamicCat',
+  );
+  RuntimeValidationTestFory.register(
+    fory,
+    DynamicAnimalEnvelope,
+    namespace: 'validation',
+    typeName: 'DynamicAnimalEnvelope',
+  );
+  RuntimeValidationTestFory.register(
+    fory,
+    SkipEnvelope,
+    namespace: 'validation',
+    typeName: 'SkipEnvelope',
+  );
+}
+
+void _registerSkipV1(Fory fory) {
+  RuntimeValidationTestFory.register(
+    fory,
+    SkipCompatibleV1,
+    namespace: 'validation',
+    typeName: 'SkipCompatible',
+  );
+}
+
+void _registerSkipV2(Fory fory) {
+  RuntimeValidationTestFory.register(
+    fory,
+    SkipCompatibleV2,
+    namespace: 'validation',
+    typeName: 'SkipCompatible',
+  );
+}
+
+void _registerSchemaV1(Fory fory) {
+  RuntimeValidationTestFory.register(
+    fory,
+    SchemaVersionV1,
+    namespace: 'validation',
+    typeName: 'SchemaVersion',
+  );
+}
+
+void _registerSchemaV2(Fory fory) {
+  RuntimeValidationTestFory.register(
+    fory,
+    SchemaVersionV2,
+    namespace: 'validation',
+    typeName: 'SchemaVersion',
+  );
+}
+
+Object _nestedList(int depth) {
+  Object value = 'leaf';
+  for (var index = 0; index < depth; index += 1) {
+    value = <Object?>[value];
+  }
+  return value;
+}
+
+void main() {
+  group('field options', () {
+    test('skip fields stay local only after round trip', () {
+      final fory = Fory();
+      _registerValidationTypes(fory);
+
+      final roundTrip = fory.deserialize<SkipEnvelope>(
+        fory.serialize(
+          SkipEnvelope()
+            ..visible = 'kept'
+            ..skipped = 'discarded',
+        ),
+      );
+
+      expect(roundTrip.visible, equals('kept'));
+      expect(roundTrip.skipped, equals('local-default'));
+    });
+
+    test('dynamic fields preserve concrete runtime payload types', () {
+      final fory = Fory();
+      _registerValidationTypes(fory);
+
+      final dogEnvelope = fory.deserialize<DynamicAnimalEnvelope>(
+        fory.serialize(
+          DynamicAnimalEnvelope()..animal = (DynamicDog()..name = 'Rex'),
+        ),
+      );
+      final catEnvelope = fory.deserialize<DynamicAnimalEnvelope>(
+        fory.serialize(
+          DynamicAnimalEnvelope()
+            ..animal = (DynamicCat()
+              ..name = 'Misty'
+              ..lives = 7),
+        ),
+      );
+
+      expect(dogEnvelope.animal, isA<DynamicDog>());
+      expect((dogEnvelope.animal as DynamicDog).name, equals('Rex'));
+      expect(catEnvelope.animal, isA<DynamicCat>());
+      expect((catEnvelope.animal as DynamicCat).name, equals('Misty'));
+      expect((catEnvelope.animal as DynamicCat).lives, equals(7));
+    });
+
+    test('compatible mode ignores skipped fields from older writers', () {
+      final writer = Fory(compatible: true);
+      final reader = Fory(compatible: true);
+      _registerSkipV1(writer);
+      _registerSkipV2(reader);
+
+      final migrated = reader.deserialize<SkipCompatibleV2>(
+        writer.serialize(
+          SkipCompatibleV1()
+            ..visible = 'seen'
+            ..skipped = 'legacy',
+        ),
+      );
+
+      expect(migrated.visible, equals('seen'));
+      expect(migrated.skipped, equals('reader-default'));
+    });
+  });
+
+  group('runtime validation', () {
+    test('rejects non-xlang payload headers', () {
+      final fory = Fory();
+
+      expect(
+        () => fory.deserialize<Object?>(Uint8List.fromList(<int>[0])),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Only xlang payloads are supported by the Dart runtime.'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects post-read type mismatches', () {
+      final fory = Fory();
+      final bytes = fory.serialize('value');
+
+      expect(
+        () => fory.deserialize<Int32>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('expected Int32'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects unregistered generated and manual values', () {
+      final fory = Fory();
+
+      expect(
+        () => fory.serialize(FreshGeneratedValue()..value = 'generated'),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Type FreshGeneratedValue is not registered.'),
+          ),
+        ),
+      );
+      expect(
+        () => fory.serialize(PlainManualValue('manual')),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Type PlainManualValue is not registered.'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects missing generated metadata and invalid registration modes',
+        () {
+      final fory = Fory();
+
+      expect(
+        () => fory.register(
+          FreshGeneratedValue,
+          namespace: 'validation',
+          typeName: 'FreshGeneratedValue',
+        ),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('has no generated registration metadata'),
+          ),
+        ),
+      );
+      expect(
+        () => fory.registerSerializer(
+          PlainManualValue,
+          const PlainManualValueSerializer(),
+        ),
+        throwsA(
+          isA<ArgumentError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Exactly one registration mode is required'),
+          ),
+        ),
+      );
+      expect(
+        () => fory.registerSerializer(
+          PlainManualValue,
+          const PlainManualValueSerializer(),
+          namespace: 'validation',
+        ),
+        throwsA(
+          isA<ArgumentError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Both namespace and typeName are required'),
+          ),
+        ),
+      );
+      expect(
+        () => fory.registerSerializer(
+          PlainManualValue,
+          const PlainManualValueSerializer(),
+          id: 1,
+          namespace: 'validation',
+          typeName: 'PlainManualValue',
+        ),
+        throwsA(
+          isA<ArgumentError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Exactly one registration mode is required'),
+          ),
+        ),
+      );
+    });
+
+    test('enforces maxDepth during write and read', () {
+      final nested = _nestedList(4);
+      final bytes = Fory().serialize(nested);
+
+      expect(
+        () => Fory(maxDepth: 3).serialize(nested),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Serialization depth exceeded 3.'),
+          ),
+        ),
+      );
+      expect(
+        () => Fory(maxDepth: 3).deserialize<Object?>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Deserialization depth exceeded 3.'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects schema version mismatches in schema-consistent mode', () {
+      final writer = Fory();
+      final reader = Fory();
+      _registerSchemaV1(writer);
+      _registerSchemaV2(reader);
+
+      final bytes = writer.serialize(SchemaVersionV1()..label = 'alpha');
+
+      expect(
+        () => reader.deserialize<SchemaVersionV2>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Struct schema version mismatch'),
+          ),
+        ),
+      );
+    });
+  });
+}
diff --git a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart
new file mode 100644
index 000000000..2a03eb080
--- /dev/null
+++ b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart
@@ -0,0 +1,423 @@
+/*
+ * 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.
+ */
+
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+part 'scalar_and_typed_array_serializer_test.fory.dart';
+
+@ForyStruct()
+class ScalarAndArrayEnvelope {
+  ScalarAndArrayEnvelope();
+
+  Uint8List binary = Uint8List(0);
+  Int8List int8s = Int8List(0);
+  Int16List int16s = Int16List(0);
+  Int32List int32s = Int32List(0);
+  Int64List int64s = Int64List(0);
+  Uint16List uint16s = Uint16List(0);
+  Uint32List uint32s = Uint32List(0);
+  Uint64List uint64s = Uint64List(0);
+  Float16List float16s = Float16List(0);
+  Bfloat16List bfloat16s = Bfloat16List(0);
+  Float32List float32s = Float32List(0);
+  Float64List float64s = Float64List(0);
+  List<bool> flags = <bool>[];
+  Float16 half = Float16(0);
+  Bfloat16 brain = Bfloat16(0);
+  Float32 single = Float32(0);
+  LocalDate date = const LocalDate(1970, 1, 1);
+  Timestamp timestamp = const Timestamp(0, 0);
+}
+
+void _registerScalarTypes(Fory fory) {
+  ScalarAndTypedArraySerializerTestFory.register(
+    fory,
+    ScalarAndArrayEnvelope,
+    namespace: 'test',
+    typeName: 'ScalarAndArrayEnvelope',
+  );
+}
+
+T _roundTripRoot<T>(Fory fory, Object value) {
+  return fory.deserialize<T>(fory.serialize(value));
+}
+
+ScalarAndArrayEnvelope _sampleEnvelope() {
+  return ScalarAndArrayEnvelope()
+    ..binary = Uint8List.fromList(<int>[0, 1, 2, 255])
+    ..int8s = Int8List.fromList(<int>[-128, -1, 0, 127])
+    ..int16s = Int16List.fromList(<int>[-32768, -1, 0, 32767])
+    ..int32s = Int32List.fromList(<int>[-1, 0, 1, 123456789])
+    ..int64s = Int64List.fromList(<int>[-1, 0, 1, 1 << 40])
+    ..uint16s = Uint16List.fromList(<int>[0, 1, 65535])
+    ..uint32s = Uint32List.fromList(<int>[0, 1, 0x7fffffff])
+    ..uint64s = Uint64List.fromList(<int>[0, 1, 1 << 40])
+    ..float16s = Float16List.fromList(<Float16>[
+      Float16.fromBits(0x8000),
+      Float16.fromBits(0x3555),
+      Float16.fromBits(0x7c00),
+    ])
+    ..bfloat16s = Bfloat16List.fromList(<Bfloat16>[
+      Bfloat16.fromBits(0x8000),
+      Bfloat16.fromBits(0x3eab),
+      Bfloat16.fromBits(0x7f80),
+    ])
+    ..float32s = Float32List.fromList(<double>[-1.5, 0.0, 3.25])
+    ..float64s = Float64List.fromList(<double>[-2.5, 0.0, 4.5])
+    ..flags = <bool>[true, false, true, true]
+    ..half = const Float16.fromBits(0x8000)
+    ..brain = const Bfloat16.fromBits(0x7fc0)
+    ..single = Float32(3.5)
+    ..date = LocalDate.fromEpochDay(-1)
+    ..timestamp = const Timestamp(-123, 456789123);
+}
+
+void _expectUint8ListEquals(Uint8List actual, Uint8List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectInt8ListEquals(Int8List actual, Int8List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectInt16ListEquals(Int16List actual, Int16List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectInt32ListEquals(Int32List actual, Int32List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectInt64ListEquals(Int64List actual, Int64List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectUint16ListEquals(Uint16List actual, Uint16List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectUint32ListEquals(Uint32List actual, Uint32List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectUint64ListEquals(Uint64List actual, Uint64List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectFloat16ListEquals(Float16List actual, Float16List expected) {
+  expect(
+    actual.map((value) => value.toBits()).toList(),
+    orderedEquals(expected.map((value) => value.toBits()).toList()),
+  );
+}
+
+void _expectBfloat16ListEquals(Bfloat16List actual, Bfloat16List expected) {
+  expect(
+    actual.map((value) => value.toBits()).toList(),
+    orderedEquals(expected.map((value) => value.toBits()).toList()),
+  );
+}
+
+void _expectFloat32ListEquals(Float32List actual, Float32List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void _expectFloat64ListEquals(Float64List actual, Float64List expected) {
+  expect(actual.toList(), orderedEquals(expected.toList()));
+}
+
+void main() {
+  group('scalar and typed array serializers', () {
+    test('round-trips Float16 edge cases', () {
+      final fory = Fory();
+      final cases = <Float16>[
+        const Float16.fromBits(0x8000),
+        const Float16.fromBits(0x7e00),
+        Float16(double.infinity),
+        Float16(-1.5),
+      ];
+
+      for (final value in cases) {
+        final roundTrip = _roundTripRoot<Float16>(fory, value);
+        expect(roundTrip.toBits(), equals(value.toBits()));
+      }
+    });
+
+    test('round-trips Float32 edge cases', () {
+      final fory = Fory();
+
+      final negativeZero = _roundTripRoot<Float32>(fory, Float32(-0.0));
+      final nanValue = _roundTripRoot<Float32>(fory, Float32(double.nan));
+      final infinity = _roundTripRoot<Float32>(fory, Float32(double.infinity));
+      final ordinary = _roundTripRoot<Float32>(fory, Float32(-1.5));
+
+      expect(negativeZero.value, equals(0.0));
+      expect(1 / negativeZero.value, equals(double.negativeInfinity));
+      expect(nanValue.value.isNaN, isTrue);
+      expect(infinity.value, equals(double.infinity));
+      expect(ordinary, equals(Float32(-1.5)));
+    });
+
+    test('round-trips Bfloat16 edge cases', () {
+      final fory = Fory();
+      final cases = <Bfloat16>[
+        const Bfloat16.fromBits(0x8000),
+        const Bfloat16.fromBits(0x7fc0),
+        Bfloat16(double.infinity),
+        Bfloat16(-1.5),
+      ];
+
+      for (final value in cases) {
+        final roundTrip = _roundTripRoot<Bfloat16>(fory, value);
+        expect(roundTrip.toBits(), equals(value.toBits()));
+      }
+    });
+
+    test('round-trips LocalDate and decodes root timestamps as DateTime', () {
+      final fory = Fory();
+
+      final beforeEpoch = _roundTripRoot<LocalDate>(
+        fory,
+        LocalDate.fromEpochDay(-1),
+      );
+      final leapDay = _roundTripRoot<LocalDate>(
+        fory,
+        const LocalDate(2024, 2, 29),
+      );
+      final negativeTimestamp = _roundTripRoot<DateTime>(
+        fory,
+        const Timestamp(-123, 456789000),
+      );
+      final fromDateTime = _roundTripRoot<DateTime>(
+        fory,
+        Timestamp.fromDateTime(DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700)),
+      );
+
+      expect(beforeEpoch, equals(LocalDate.fromEpochDay(-1)));
+      expect(leapDay, equals(const LocalDate(2024, 2, 29)));
+      expect(
+        negativeTimestamp,
+        equals(const Timestamp(-123, 456789000).toDateTime()),
+      );
+      expect(
+        fromDateTime,
+        equals(DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700)),
+      );
+    });
+
+    test('round-trips root typed arrays and bool arrays', () {
+      final fory = Fory();
+
+      _expectUint8ListEquals(
+        _roundTripRoot<Uint8List>(fory, Uint8List.fromList(<int>[0, 1, 255])),
+        Uint8List.fromList(<int>[0, 1, 255]),
+      );
+      _expectInt8ListEquals(
+        _roundTripRoot<Int8List>(
+          fory,
+          Int8List.fromList(<int>[-128, -1, 0, 127]),
+        ),
+        Int8List.fromList(<int>[-128, -1, 0, 127]),
+      );
+      _expectInt16ListEquals(
+        _roundTripRoot<Int16List>(
+          fory,
+          Int16List.fromList(<int>[-32768, -1, 0, 32767]),
+        ),
+        Int16List.fromList(<int>[-32768, -1, 0, 32767]),
+      );
+      _expectInt32ListEquals(
+        _roundTripRoot<Int32List>(
+          fory,
+          Int32List.fromList(<int>[-1, 0, 1, 123456789]),
+        ),
+        Int32List.fromList(<int>[-1, 0, 1, 123456789]),
+      );
+      _expectInt64ListEquals(
+        _roundTripRoot<Int64List>(
+          fory,
+          Int64List.fromList(<int>[-1, 0, 1, 1 << 40]),
+        ),
+        Int64List.fromList(<int>[-1, 0, 1, 1 << 40]),
+      );
+      _expectUint16ListEquals(
+        _roundTripRoot<Uint16List>(
+          fory,
+          Uint16List.fromList(<int>[0, 1, 65535]),
+        ),
+        Uint16List.fromList(<int>[0, 1, 65535]),
+      );
+      _expectUint32ListEquals(
+        _roundTripRoot<Uint32List>(
+          fory,
+          Uint32List.fromList(<int>[0, 1, 0x7fffffff]),
+        ),
+        Uint32List.fromList(<int>[0, 1, 0x7fffffff]),
+      );
+      _expectUint64ListEquals(
+        _roundTripRoot<Uint64List>(
+          fory,
+          Uint64List.fromList(<int>[0, 1, 1 << 40]),
+        ),
+        Uint64List.fromList(<int>[0, 1, 1 << 40]),
+      );
+      _expectFloat16ListEquals(
+        _roundTripRoot<Float16List>(
+          fory,
+          Float16List.fromList(<Float16>[
+            Float16.fromBits(0x8000),
+            Float16.fromBits(0x3555),
+            Float16.fromBits(0x7c00),
+          ]),
+        ),
+        Float16List.fromList(<Float16>[
+          Float16.fromBits(0x8000),
+          Float16.fromBits(0x3555),
+          Float16.fromBits(0x7c00),
+        ]),
+      );
+      _expectBfloat16ListEquals(
+        _roundTripRoot<Bfloat16List>(
+          fory,
+          Bfloat16List.fromList(<Bfloat16>[
+            Bfloat16.fromBits(0x8000),
+            Bfloat16.fromBits(0x3eab),
+            Bfloat16.fromBits(0x7f80),
+          ]),
+        ),
+        Bfloat16List.fromList(<Bfloat16>[
+          Bfloat16.fromBits(0x8000),
+          Bfloat16.fromBits(0x3eab),
+          Bfloat16.fromBits(0x7f80),
+        ]),
+      );
+      _expectFloat32ListEquals(
+        _roundTripRoot<Float32List>(
+          fory,
+          Float32List.fromList(<double>[-1.5, 0.0, 3.25]),
+        ),
+        Float32List.fromList(<double>[-1.5, 0.0, 3.25]),
+      );
+      _expectFloat64ListEquals(
+        _roundTripRoot<Float64List>(
+          fory,
+          Float64List.fromList(<double>[-2.5, 0.0, 4.5]),
+        ),
+        Float64List.fromList(<double>[-2.5, 0.0, 4.5]),
+      );
+      expect(
+        _roundTripRoot<List<bool>>(fory, <bool>[true, false, true]),
+        orderedEquals(<bool>[true, false, true]),
+      );
+    });
+
+    test('round-trips empty binary and typed array payloads', () {
+      final fory = Fory();
+
+      expect(_roundTripRoot<Uint8List>(fory, Uint8List(0)), isEmpty);
+      expect(_roundTripRoot<Int8List>(fory, Int8List(0)), isEmpty);
+      expect(_roundTripRoot<Float16List>(fory, Float16List(0)), isEmpty);
+      expect(_roundTripRoot<Bfloat16List>(fory, Bfloat16List(0)), isEmpty);
+      expect(_roundTripRoot<Float32List>(fory, Float32List(0)), isEmpty);
+      expect(_roundTripRoot<List<bool>>(fory, <bool>[]), isEmpty);
+    });
+
+    test('Float16List and Bfloat16List expose contiguous typed-data views', () {
+      final storage =
+          Uint16List.fromList(<int>[0x3c00, 0x3555, 0x3f80, 0x7fc0]);
+
+      final float16s = Float16List.view(storage.buffer, 0, 2);
+      final bfloat16s = Bfloat16List.sublistView(storage, 2, 4);
+
+      expect(float16s.offsetInBytes, equals(0));
+      expect(float16s.lengthInBytes, equals(4));
+      expect(float16s[0].toBits(), equals(0x3c00));
+      expect(float16s[1].toBits(), equals(0x3555));
+
+      float16s[1] = Float16.fromBits(0x4200);
+      expect(storage[1], equals(0x4200));
+
+      expect(bfloat16s.offsetInBytes, equals(4));
+      expect(bfloat16s[0].toBits(), equals(0x3f80));
+      expect(bfloat16s[1].toBits(), equals(0x7fc0));
+
+      bfloat16s[0] = Bfloat16.fromBits(0xbf80);
+      expect(storage[2], equals(0xbf80));
+    });
+
+    test('round-trips generated struct fields for arrays and scalars', () {
+      final fory = Fory();
+      _registerScalarTypes(fory);
+
+      final value = _sampleEnvelope();
+      final roundTrip = fory.deserialize<ScalarAndArrayEnvelope>(
+        fory.serialize(value),
+      );
+
+      _expectUint8ListEquals(roundTrip.binary, value.binary);
+      _expectInt8ListEquals(roundTrip.int8s, value.int8s);
+      _expectInt16ListEquals(roundTrip.int16s, value.int16s);
+      _expectInt32ListEquals(roundTrip.int32s, value.int32s);
+      _expectInt64ListEquals(roundTrip.int64s, value.int64s);
+      _expectUint16ListEquals(roundTrip.uint16s, value.uint16s);
+      _expectUint32ListEquals(roundTrip.uint32s, value.uint32s);
+      _expectUint64ListEquals(roundTrip.uint64s, value.uint64s);
+      _expectFloat16ListEquals(roundTrip.float16s, value.float16s);
+      _expectBfloat16ListEquals(roundTrip.bfloat16s, value.bfloat16s);
+      _expectFloat32ListEquals(roundTrip.float32s, value.float32s);
+      _expectFloat64ListEquals(roundTrip.float64s, value.float64s);
+      expect(roundTrip.flags, orderedEquals(value.flags));
+      expect(roundTrip.half.toBits(), equals(value.half.toBits()));
+      expect(roundTrip.brain.toBits(), equals(value.brain.toBits()));
+      expect(roundTrip.single, equals(value.single));
+      expect(roundTrip.date, equals(value.date));
+      expect(roundTrip.timestamp, equals(value.timestamp));
+    });
+
+    test('enforces maxBinarySize on write and read', () {
+      final oversized = Uint8List.fromList(<int>[1, 2, 3, 4]);
+
+      expect(
+        () => Fory(maxBinarySize: 3).serialize(oversized),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Binary payload exceeds 3 bytes.'),
+          ),
+        ),
+      );
+
+      final bytes = Fory().serialize(oversized);
+      expect(
+        () => Fory(maxBinarySize: 3).deserialize<Uint8List>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Binary payload exceeds 3 bytes.'),
+          ),
+        ),
+      );
+    });
+  });
+}
diff --git a/dart/packages/fory/test/string_serializer_test.dart b/dart/packages/fory/test/string_serializer_test.dart
new file mode 100644
index 000000000..7ab15f8f7
--- /dev/null
+++ b/dart/packages/fory/test/string_serializer_test.dart
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ */
+
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:fory/src/string_encoding.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('string payload encoding', () {
+    test('writes latin1 payloads for ascii and latin1 strings', () {
+      final cases = <String>[
+        '',
+        'a',
+        'hello world',
+        'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
+        'café',
+        'résumé',
+        'Ñoño',
+        '\u00A0\u00FF',
+        'hello \u00E9 world',
+        'line1\nline2\rline3\tline4',
+        _repeat('ÿ', 40),
+      ];
+
+      for (final value in cases) {
+        final payload = _writeAndReadPayload(value);
+        expect(payload.encoding, equals(stringLatin1Encoding), reason: value);
+        expect(payload.byteLength, equals(value.length), reason: value);
+        expect(payload.decoded, equals(value), reason: value);
+      }
+    });
+
+    test('writes utf8 payloads for unicode, mixed, and large strings', () {
+      final cases = <String>[
+        '你好',
+        'Hello 世界',
+        'こんにちは',
+        '안녕하세요',
+        'Привет',
+        '😀😁😂',
+        'hello café 你好',
+        'test\u00E9test你test',
+        '🎉hello世界café🎊',
+        _repeat('你', 30),
+        '${_repeat('a', 32)}你好${_repeat('b', 32)}',
+        '${_repeat('x', 500)}${_repeat('你', 250)}',
+      ];
+
+      for (final value in cases) {
+        final payload = _writeAndReadPayload(value);
+        expect(payload.encoding, equals(stringUtf8Encoding), reason: value);
+        expect(
+          payload.byteLength,
+          equals(utf8.encode(value).length),
+          reason: value,
+        );
+        expect(payload.decoded, equals(value), reason: value);
+      }
+    });
+
+    test('normalizes unmatched surrogates to replacement characters', () {
+      final cases = <({String expected, String input})>[
+        (input: '\uD800', expected: '\uFFFD'),
+        (input: 'x\uD800y', expected: 'x\uFFFDy'),
+        (input: '\uDC00', expected: '\uFFFD'),
+        (input: '\uD83Dabc', expected: '\uFFFDabc'),
+        (input: 'abc\uDE00', expected: 'abc\uFFFD'),
+      ];
+
+      for (final testCase in cases) {
+        final payload = _writeAndReadPayload(testCase.input);
+        expect(
+          payload.encoding,
+          equals(stringUtf8Encoding),
+          reason: testCase.input.runes.toList().toString(),
+        );
+        expect(payload.decoded, equals(testCase.expected));
+      }
+    });
+
+    test('reads utf16 payloads from cross-language wire format', () {
+      final cases = <String>[
+        '',
+        '你好',
+        'Hello 世界',
+        'こんにちは',
+        '🎉🎊',
+        'alpha\u0000omega',
+      ];
+
+      for (final value in cases) {
+        final utf16Bytes = _utf16LeBytes(value);
+        final buffer = Buffer();
+        buffer.writeVarUint36Small(
+          (utf16Bytes.length << 2) | stringUtf16Encoding,
+        );
+        buffer.writeBytes(utf16Bytes);
+
+        final payload = _readPayload(buffer.toBytes());
+        expect(payload.encoding, equals(stringUtf16Encoding), reason: value);
+        expect(payload.byteLength, equals(utf16Bytes.length), reason: value);
+        expect(payload.decoded, equals(value), reason: value);
+      }
+    });
+
+    test('handles payload header boundaries and utf8 header compaction', () {
+      final cases =
+          <({int byteLength, int encoding, int headerLength, String value})>[
+        (
+          value: _repeat('a', 31),
+          encoding: stringLatin1Encoding,
+          byteLength: 31,
+          headerLength: 1
+        ),
+        (
+          value: _repeat('a', 32),
+          encoding: stringLatin1Encoding,
+          byteLength: 32,
+          headerLength: 2
+        ),
+        (
+          value: _repeat('😀', 7),
+          encoding: stringUtf8Encoding,
+          byteLength: 28,
+          headerLength: 1
+        ),
+        (
+          value: _repeat('😀', 8),
+          encoding: stringUtf8Encoding,
+          byteLength: 32,
+          headerLength: 2
+        ),
+        (
+          value: _repeat('你', 31),
+          encoding: stringUtf8Encoding,
+          byteLength: 93,
+          headerLength: 2
+        ),
+      ];
+
+      for (final testCase in cases) {
+        final payload = _writeAndReadPayload(testCase.value);
+        expect(payload.encoding, equals(testCase.encoding),
+            reason: testCase.value);
+        expect(payload.byteLength, equals(testCase.byteLength),
+            reason: testCase.value);
+        expect(
+          payload.bytes.length - payload.byteLength,
+          equals(testCase.headerLength),
+          reason: testCase.value,
+        );
+        expect(payload.decoded, equals(testCase.value), reason: testCase.value);
+      }
+    });
+
+    test('reuses the same buffer across a varied string sequence', () {
+      final buffer = Buffer();
+      final cases = <({String expected, String input})>[
+        (input: 'short', expected: 'short'),
+        (input: _repeat('a', 1000), expected: _repeat('a', 1000)),
+        (input: 'short again', expected: 'short again'),
+        (input: 'café', expected: 'café'),
+        (input: '你好', expected: '你好'),
+        (input: _repeat('é', 500), expected: _repeat('é', 500)),
+        (input: 'abc\uDE00', expected: 'abc\uFFFD'),
+        (input: 'final 😀 test', expected: 'final 😀 test'),
+      ];
+
+      for (final testCase in cases) {
+        buffer.clear();
+        writeString(buffer, testCase.input);
+        final payload = _readPayload(buffer.toBytes());
+        expect(payload.decoded, equals(testCase.expected));
+      }
+    });
+  });
+
+  group('string serializer', () {
+    test('round-trips root strings across unicode categories', () {
+      final fory = Fory();
+      final cases = <({String expected, String input})>[
+        (input: '', expected: ''),
+        (input: 'Apache Fory', expected: 'Apache Fory'),
+        (input: 'café', expected: 'café'),
+        (input: '你好,Fory🙂', expected: '你好,Fory🙂'),
+        (input: 'hello café 你好', expected: 'hello café 你好'),
+        (input: '\n\r\t', expected: '\n\r\t'),
+        (input: '\u0000', expected: '\u0000'),
+        (input: _repeat('😀', 64), expected: _repeat('😀', 64)),
+        (
+          input: '${_repeat('x', 500)}${_repeat('你', 500)}',
+          expected: '${_repeat('x', 500)}${_repeat('你', 500)}'
+        ),
+        (input: 'x\uD800y', expected: 'x\uFFFDy'),
+      ];
+
+      for (final testCase in cases) {
+        final roundTrip =
+            fory.deserialize<String>(fory.serialize(testCase.input));
+        expect(roundTrip, equals(testCase.expected));
+      }
+    });
+
+    test('round-trips string collections with mixed content', () {
+      final fory = Fory();
+      final values = <String>[
+        '',
+        'alpha',
+        'café',
+        '你好',
+        '😀',
+        'line1\nline2',
+        'x\uD800y',
+        _repeat('你', 64),
+      ];
+      final expected = <String>[
+        '',
+        'alpha',
+        'café',
+        '你好',
+        '😀',
+        'line1\nline2',
+        'x\uFFFDy',
+        _repeat('你', 64),
+      ];
+
+      final roundTrip = fory.deserialize<Object?>(
+        fory.serialize(values, trackRef: true),
+      ) as List<Object?>;
+
+      expect(roundTrip, orderedEquals(expected));
+    });
+
+    test('reuses the same fory instance and target buffer across strings', () {
+      final fory = Fory();
+      final buffer = Buffer();
+      final cases = <({String expected, String input})>[
+        (input: 'short', expected: 'short'),
+        (input: _repeat('a', 1000), expected: _repeat('a', 1000)),
+        (input: 'こんにちは', expected: 'こんにちは'),
+        (input: 'abc\uDE00', expected: 'abc\uFFFD'),
+        (input: 'final test', expected: 'final test'),
+      ];
+
+      for (final testCase in cases) {
+        fory.serializeTo(testCase.input, buffer);
+        final roundTrip = fory.deserializeFrom<String>(buffer);
+        expect(roundTrip, equals(testCase.expected));
+        expect(buffer.readableBytes, equals(0));
+      }
+    });
+  });
+}
+
+({Uint8List bytes, int encoding, int byteLength, String decoded})
+    _writeAndReadPayload(
+  String value,
+) {
+  final buffer = Buffer();
+  writeString(buffer, value);
+  return _readPayload(buffer.toBytes());
+}
+
+({Uint8List bytes, int encoding, int byteLength, String decoded}) _readPayload(
+  Uint8List bytes,
+) {
+  final buffer = Buffer.wrap(Uint8List.fromList(bytes));
+  final header = buffer.readVarUint36Small();
+  final encoding = header & 0x03;
+  final byteLength = header >>> 2;
+  final decoded = readStringFromBuffer(buffer, byteLength, encoding);
+  expect(buffer.readableBytes, equals(0));
+  return (
+    bytes: bytes,
+    encoding: encoding,
+    byteLength: byteLength,
+    decoded: decoded,
+  );
+}
+
+Uint8List _utf16LeBytes(String value) {
+  final codeUnits = value.codeUnits;
+  final bytes = Uint8List(codeUnits.length * 2);
+  final view = ByteData.sublistView(bytes);
+  for (var index = 0; index < codeUnits.length; index += 1) {
+    view.setUint16(index * 2, codeUnits[index], Endian.little);
+  }
+  return bytes;
+}
+
+String _repeat(String value, int count) =>
+    List<String>.filled(count, value).join();
diff --git a/dart/packages/fory/test/time_serializer_test.dart b/dart/packages/fory/test/time_serializer_test.dart
new file mode 100644
index 000000000..585e67001
--- /dev/null
+++ b/dart/packages/fory/test/time_serializer_test.dart
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ */
+
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+part 'time_serializer_test.fory.dart';
+
+@ForyStruct()
+class TimeEnvelope {
+  TimeEnvelope();
+
+  LocalDate date = const LocalDate(1970, 1, 1);
+  Timestamp timestamp = const Timestamp(0, 0);
+  DateTime instant = DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true);
+  Duration duration = Duration.zero;
+  LocalDate? optionalDate;
+  Timestamp? optionalTimestamp;
+  DateTime? optionalInstant;
+  Duration? optionalDuration;
+}
+
+void _registerTimeTypes(Fory fory) {
+  TimeSerializerTestFory.register(
+    fory,
+    TimeEnvelope,
+    namespace: 'time',
+    typeName: 'TimeEnvelope',
+  );
+}
+
+void _expectTimeEnvelope(TimeEnvelope actual, TimeEnvelope expected) {
+  expect(actual.date, equals(expected.date));
+  expect(actual.timestamp, equals(expected.timestamp));
+  expect(actual.instant, equals(expected.instant));
+  expect(actual.duration, equals(expected.duration));
+  expect(actual.optionalDate, equals(expected.optionalDate));
+  expect(actual.optionalTimestamp, equals(expected.optionalTimestamp));
+  expect(actual.optionalInstant, equals(expected.optionalInstant));
+  expect(actual.optionalDuration, equals(expected.optionalDuration));
+}
+
+TimeEnvelope _sampleTimeEnvelope() {
+  return TimeEnvelope()
+    ..date = const LocalDate(2024, 2, 29)
+    ..timestamp = const Timestamp(-123456789, 987654321)
+    ..instant = DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true)
+    ..duration = const Duration(days: 2, seconds: 3, microseconds: 456789)
+    ..optionalDate = LocalDate.fromEpochDay(-1)
+    ..optionalTimestamp = Timestamp.fromDateTime(
+      DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700),
+    )
+    ..optionalInstant = DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700)
+    ..optionalDuration = const Duration(microseconds: -1500001);
+}
+
+void main() {
+  group('time serializers', () {
+    test('round-trips LocalDate edge cases', () {
+      final fory = Fory();
+      final cases = <LocalDate>[
+        LocalDate.fromEpochDay(-1),
+        LocalDate.fromEpochDay(0),
+        const LocalDate(2024, 2, 29),
+        const LocalDate(9999, 12, 31),
+      ];
+
+      for (final value in cases) {
+        expect(
+            fory.deserialize<LocalDate>(fory.serialize(value)), equals(value));
+      }
+    });
+
+    test('decodes root Timestamp payloads to DateTime by default', () {
+      final fory = Fory();
+      final cases = <MapEntry<Timestamp, DateTime>>[
+        MapEntry(
+          const Timestamp(0, 0),
+          DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true),
+        ),
+        MapEntry(
+          const Timestamp(-1, 1000),
+          DateTime.fromMicrosecondsSinceEpoch(-999999, isUtc: true),
+        ),
+        MapEntry(
+          const Timestamp(1, 999999000),
+          DateTime.fromMicrosecondsSinceEpoch(1999999, isUtc: true),
+        ),
+        MapEntry(
+          Timestamp.fromDateTime(DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700)),
+          DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700),
+        ),
+      ];
+
+      for (final entry in cases) {
+        final roundTrip = fory.deserialize<Object?>(fory.serialize(entry.key));
+
+        expect(roundTrip, isA<DateTime>());
+        expect(roundTrip, equals(entry.value));
+      }
+    });
+
+    test('round-trips DateTime edge cases as timestamp payloads', () {
+      final fory = Fory();
+      final cases = <DateTime>[
+        DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true),
+        DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true),
+        DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700),
+      ];
+
+      for (final value in cases) {
+        expect(
+            fory.deserialize<DateTime>(fory.serialize(value)), equals(value));
+      }
+    });
+
+    test(
+        'root containers without declared element types decode timestamps as DateTime',
+        () {
+      final fory = Fory();
+      final roundTrip = fory.deserialize<List<Object?>>(
+        fory.serialize(<Object>[const Timestamp(-1, 999999000)]),
+      );
+
+      expect(
+        roundTrip.single,
+        isA<DateTime>().having(
+          (value) => value,
+          'value',
+          equals(DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true)),
+        ),
+      );
+    });
+
+    test('reads timestamp payloads directly as DateTime', () {
+      final fory = Fory();
+
+      expect(
+        fory.deserialize<DateTime>(
+            fory.serialize(const Timestamp(-1, 999999000))),
+        equals(DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true)),
+      );
+    });
+
+    test('normalizes local DateTime values to UTC instants', () {
+      final fory = Fory();
+      final value = DateTime(2024, 1, 2, 3, 4, 5, 6, 700);
+
+      final roundTrip = fory.deserialize<DateTime?>(fory.serialize(value));
+
+      expect(roundTrip, isNotNull);
+      expect(roundTrip!.isUtc, isTrue);
+      expect(roundTrip.isAtSameMomentAs(value), isTrue);
+    });
+
+    test('converts negative DateTime values to Timestamp correctly', () {
+      final cases = <MapEntry<DateTime, Timestamp>>[
+        MapEntry(
+          DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true),
+          const Timestamp(-1, 999999000),
+        ),
+        MapEntry(
+          DateTime.fromMicrosecondsSinceEpoch(-1000001, isUtc: true),
+          const Timestamp(-2, 999999000),
+        ),
+      ];
+
+      for (final entry in cases) {
+        expect(Timestamp.fromDateTime(entry.key), equals(entry.value));
+        expect(entry.value.toDateTime(), equals(entry.key));
+      }
+    });
+
+    test('round-trips Duration edge cases', () {
+      final fory = Fory();
+      final cases = <Duration>[
+        Duration.zero,
+        const Duration(microseconds: 1),
+        const Duration(seconds: 1, microseconds: 234567),
+        const Duration(microseconds: -1),
+        const Duration(microseconds: -1500001),
+        const Duration(days: 7, seconds: 3, microseconds: 999999),
+      ];
+
+      for (final value in cases) {
+        expect(
+            fory.deserialize<Duration>(fory.serialize(value)), equals(value));
+      }
+    });
+
+    test('round-trips generated time fields in schema-consistent mode', () {
+      final fory = Fory();
+      _registerTimeTypes(fory);
+
+      final value = _sampleTimeEnvelope();
+      final roundTrip = fory.deserialize<TimeEnvelope>(fory.serialize(value));
+
+      _expectTimeEnvelope(roundTrip, value);
+    });
+
+    test('round-trips generated time fields in compatible mode', () {
+      final fory = Fory(compatible: true);
+      _registerTimeTypes(fory);
+
+      final value = _sampleTimeEnvelope();
+      final roundTrip = fory.deserialize<TimeEnvelope>(fory.serialize(value));
+
+      _expectTimeEnvelope(roundTrip, value);
+    });
+
+    test('does not preserve references for repeated temporal values', () {
+      final fory = Fory();
+      final duration = Duration(microseconds: 1);
+      final timestamp = Timestamp(-1, 1000);
+      final date = LocalDate.fromEpochDay(-1);
+
+      final roundTrip = fory.deserialize<List<Object?>>(
+        fory.serialize(
+          <Object>[duration, duration, timestamp, timestamp, date, date],
+          trackRef: true,
+        ),
+      );
+
+      expect(roundTrip[0], equals(roundTrip[1]));
+      expect(identical(roundTrip[0], roundTrip[1]), isFalse);
+      expect(roundTrip[2], isA<DateTime>());
+      expect(roundTrip[2], equals(roundTrip[3]));
+      expect(identical(roundTrip[2], roundTrip[3]), isFalse);
+      expect(roundTrip[4], equals(roundTrip[5]));
+      expect(identical(roundTrip[4], roundTrip[5]), isFalse);
+    });
+
+    test('supports null optional time fields', () {
+      final fory = Fory();
+      _registerTimeTypes(fory);
+
+      final roundTrip = fory.deserialize<TimeEnvelope>(
+        fory.serialize(TimeEnvelope()),
+      );
+
+      expect(roundTrip.optionalDate, isNull);
+      expect(roundTrip.optionalTimestamp, isNull);
+      expect(roundTrip.optionalInstant, isNull);
+      expect(roundTrip.optionalDuration, isNull);
+    });
+
+    test('preserves sub-microsecond duration wire precision on round-trip', () {
+      final fory = Fory();
+
+      for (final nanoseconds in <int>[1, -1]) {
+        final bytes = Uint8List.fromList(fory.serialize(Duration.zero));
+        final view = ByteData.sublistView(bytes);
+        view.setInt32(bytes.length - 4, nanoseconds, Endian.little);
+
+        final roundTrip = fory.deserialize<Duration>(bytes);
+
+        expect(roundTrip, equals(Duration.zero));
+        expect(fory.serialize(roundTrip), orderedEquals(bytes));
+      }
+    });
+
+    test('rejects duration payloads with nanoseconds outside spec range', () {
+      final fory = Fory();
+
+      for (final nanoseconds in <int>[1000000000, -1000000000]) {
+        final bytes = Uint8List.fromList(fory.serialize(Duration.zero));
+        final view = ByteData.sublistView(bytes);
+        view.setInt32(bytes.length - 4, nanoseconds, Endian.little);
+
+        expect(
+          () => fory.deserialize<Duration>(bytes),
+          throwsA(
+            isA<StateError>().having(
+              (error) => error.toString(),
+              'message',
+              contains('out of range'),
+            ),
+          ),
+        );
+      }
+    });
+
+    test('rejects duration payloads with inconsistent signs', () {
+      final fory = Fory();
+      final positive =
+          Uint8List.fromList(fory.serialize(const Duration(seconds: 1)));
+      final negative =
+          Uint8List.fromList(fory.serialize(const Duration(seconds: -1)));
+
+      ByteData.sublistView(positive).setInt32(
+        positive.length - 4,
+        -1,
+        Endian.little,
+      );
+      ByteData.sublistView(negative).setInt32(
+        negative.length - 4,
+        1,
+        Endian.little,
+      );
+
+      for (final bytes in <Uint8List>[positive, negative]) {
+        expect(
+          () => fory.deserialize<Duration>(bytes),
+          throwsA(
+            isA<StateError>().having(
+              (error) => error.toString(),
+              'message',
+              contains('inconsistent signs'),
+            ),
+          ),
+        );
+      }
+    });
+
+    test('rejects invalid timestamp nanoseconds on write', () {
+      final fory = Fory();
+
+      expect(
+        () => fory.serialize(const Timestamp(0, 1000000000)),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('out of range'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects timestamp payloads with nanoseconds outside spec range', () {
+      final fory = Fory();
+      final bytes = Uint8List.fromList(fory.serialize(const Timestamp(0, 0)));
+      final view = ByteData.sublistView(bytes);
+      view.setUint32(bytes.length - 4, 1000000000, Endian.little);
+
+      expect(
+        () => fory.deserialize<Object?>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('out of range'),
+          ),
+        ),
+      );
+    });
+
+    test('rejects DateTime payloads with sub-microsecond precision', () {
+      final fory = Fory();
+      final bytes = Uint8List.fromList(
+        fory.serialize(DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true)),
+      );
+      final view = ByteData.sublistView(bytes);
+      view.setUint32(bytes.length - 4, 1, Endian.little);
+
+      expect(
+        () => fory.deserialize<DateTime>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('DateTime microsecond precision'),
+          ),
+        ),
+      );
+    });
+  });
+}
diff --git a/dart/packages/fory/test/unsigned_serializer_test.dart b/dart/packages/fory/test/unsigned_serializer_test.dart
new file mode 100644
index 000000000..7b39f7b7f
--- /dev/null
+++ b/dart/packages/fory/test/unsigned_serializer_test.dart
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+import 'package:fory/fory.dart';
+import 'package:fory/src/serializer/compatible_struct_metadata.dart';
+import 'package:test/test.dart';
+
+part 'unsigned_serializer_test.fory.dart';
+
+const int _uint32Midpoint = 0x80000000;
+const int _uint64Midpoint = 0x8000000000000000;
+const int _uint64Max = 0xffffffffffffffff;
+
+@ForyStruct()
+class UnsignedFields {
+  UnsignedFields();
+
+  @Uint8Type()
+  int u8 = 0;
+
+  @Uint16Type()
+  int u16 = 0;
+
+  @Uint32Type(compress: true)
+  int u32Var = 0;
+
+  @Uint32Type(compress: false)
+  int u32Fixed = 0;
+
+  @Uint64Type(encoding: LongEncoding.varint)
+  int u64Var = 0;
+
+  @Uint64Type(encoding: LongEncoding.fixed)
+  int u64Fixed = 0;
+
+  @Uint64Type(encoding: LongEncoding.tagged)
+  int u64Tagged = 0;
+
+  @Uint8Type()
+  int? u8Nullable;
+
+  @Uint16Type()
+  int? u16Nullable;
+
+  @Uint32Type(compress: true)
+  int? u32VarNullable;
+
+  @Uint32Type(compress: false)
+  int? u32FixedNullable;
+
+  @Uint64Type(encoding: LongEncoding.varint)
+  int? u64VarNullable;
+
+  @Uint64Type(encoding: LongEncoding.fixed)
+  int? u64FixedNullable;
+
+  @Uint64Type(encoding: LongEncoding.tagged)
+  int? u64TaggedNullable;
+}
+
+@ForyStruct()
+class UnsignedMetadataReader {
+  UnsignedMetadataReader();
+
+  @Uint8Type()
+  int u8 = 0;
+
+  int extra = 42;
+}
+
+void _registerUnsignedFields(Fory fory) {
+  UnsignedSerializerTestFory.register(
+    fory,
+    UnsignedFields,
+    namespace: 'test',
+    typeName: 'UnsignedFields',
+  );
+}
+
+void _registerUnsignedMetadataReader(Fory fory) {
+  UnsignedSerializerTestFory.register(
+    fory,
+    UnsignedMetadataReader,
+    namespace: 'test',
+    typeName: 'UnsignedFields',
+  );
+}
+
+UnsignedFields _midpointUnsignedFields() {
+  return UnsignedFields()
+    ..u8 = 0x80
+    ..u16 = 0x8000
+    ..u32Var = _uint32Midpoint
+    ..u32Fixed = _uint32Midpoint
+    ..u64Var = _uint64Midpoint
+    ..u64Fixed = _uint64Midpoint
+    ..u64Tagged = _uint64Midpoint
+    ..u8Nullable = 0x80
+    ..u16Nullable = 0x8000
+    ..u32VarNullable = _uint32Midpoint
+    ..u32FixedNullable = _uint32Midpoint
+    ..u64VarNullable = _uint64Midpoint
+    ..u64FixedNullable = _uint64Midpoint
+    ..u64TaggedNullable = _uint64Midpoint;
+}
+
+UnsignedFields _maxUnsignedFields() {
+  return UnsignedFields()
+    ..u8 = 0xff
+    ..u16 = 0xffff
+    ..u32Var = 0xffffffff
+    ..u32Fixed = 0xffffffff
+    ..u64Var = _uint64Max
+    ..u64Fixed = _uint64Max
+    ..u64Tagged = _uint64Max
+    ..u8Nullable = 0xff
+    ..u16Nullable = 0xffff
+    ..u32VarNullable = 0xffffffff
+    ..u32FixedNullable = 0xffffffff
+    ..u64VarNullable = _uint64Max
+    ..u64FixedNullable = _uint64Max
+    ..u64TaggedNullable = _uint64Max;
+}
+
+UnsignedFields _nullUnsignedFields() {
+  return UnsignedFields()
+    ..u8 = 1
+    ..u16 = 2
+    ..u32Var = 3
+    ..u32Fixed = 4
+    ..u64Var = 5
+    ..u64Fixed = 6
+    ..u64Tagged = 7
+    ..u8Nullable = null
+    ..u16Nullable = null
+    ..u32VarNullable = null
+    ..u32FixedNullable = null
+    ..u64VarNullable = null
+    ..u64FixedNullable = null
+    ..u64TaggedNullable = null;
+}
+
+void _expectUnsignedFieldsEqual(UnsignedFields actual, UnsignedFields expected) {
+  expect(actual.u8, equals(expected.u8));
+  expect(actual.u16, equals(expected.u16));
+  expect(actual.u32Var, equals(expected.u32Var));
+  expect(actual.u32Fixed, equals(expected.u32Fixed));
+  expect(actual.u64Var, equals(expected.u64Var));
+  expect(actual.u64Fixed, equals(expected.u64Fixed));
+  expect(actual.u64Tagged, equals(expected.u64Tagged));
+  expect(actual.u8Nullable, equals(expected.u8Nullable));
+  expect(actual.u16Nullable, equals(expected.u16Nullable));
+  expect(actual.u32VarNullable, equals(expected.u32VarNullable));
+  expect(actual.u32FixedNullable, equals(expected.u32FixedNullable));
+  expect(actual.u64VarNullable, equals(expected.u64VarNullable));
+  expect(actual.u64FixedNullable, equals(expected.u64FixedNullable));
+  expect(actual.u64TaggedNullable, equals(expected.u64TaggedNullable));
+}
+
+int _remoteFieldTypeId(Object value, String identifier) {
+  final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value);
+  expect(remoteTypeDef, isNotNull);
+  final field = remoteTypeDef!.fields.firstWhere(
+    (field) => field.identifier == identifier,
+  );
+  return field.fieldType.typeId;
+}
+
+bool _remoteFieldNullable(Object value, String identifier) {
+  final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value);
+  expect(remoteTypeDef, isNotNull);
+  final field = remoteTypeDef!.fields.firstWhere(
+    (field) => field.identifier == identifier,
+  );
+  return field.fieldType.nullable;
+}
+
+void main() {
+  group('unsigned generated fields', () {
+    test('round trips midpoint, max, and null boundary cases', () {
+      final fory = Fory();
+      _registerUnsignedFields(fory);
+
+      for (final value in <UnsignedFields>[
+        _midpointUnsignedFields(),
+        _maxUnsignedFields(),
+        _nullUnsignedFields(),
+      ]) {
+        final roundTrip = fory.deserialize<UnsignedFields>(fory.serialize(value));
+        _expectUnsignedFieldsEqual(roundTrip, value);
+      }
+    });
+
+    test('compatible metadata records unsigned wire types and nullability', () {
+      final writer = Fory(compatible: true);
+      final reader = Fory(compatible: true);
+      _registerUnsignedFields(writer);
+      _registerUnsignedMetadataReader(reader);
+
+      final roundTrip = reader.deserialize<UnsignedMetadataReader>(
+        writer.serialize(_maxUnsignedFields()),
+      );
+      expect(roundTrip.u8, equals(0xff));
+      expect(roundTrip.extra, equals(42));
+
+      expect(_remoteFieldTypeId(roundTrip, 'u8'), equals(TypeIds.uint8));
+      expect(_remoteFieldTypeId(roundTrip, 'u16'), equals(TypeIds.uint16));
+      expect(_remoteFieldTypeId(roundTrip, 'u32_var'), equals(TypeIds.varUint32));
+      expect(_remoteFieldTypeId(roundTrip, 'u32_fixed'), equals(TypeIds.uint32));
+      expect(_remoteFieldTypeId(roundTrip, 'u64_var'), equals(TypeIds.varUint64));
+      expect(_remoteFieldTypeId(roundTrip, 'u64_fixed'), equals(TypeIds.uint64));
+      expect(
+        _remoteFieldTypeId(roundTrip, 'u64_tagged'),
+        equals(TypeIds.taggedUint64),
+      );
+
+      expect(_remoteFieldNullable(roundTrip, 'u8'), isFalse);
+      expect(_remoteFieldNullable(roundTrip, 'u16'), isFalse);
+      expect(_remoteFieldNullable(roundTrip, 'u8_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u16_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u32_var_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u32_fixed_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u64_var_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u64_fixed_nullable'), isTrue);
+      expect(_remoteFieldNullable(roundTrip, 'u64_tagged_nullable'), isTrue);
+    });
+  });
+}
diff --git a/dart/packages/fory/test/xlang_protocol_test.dart b/dart/packages/fory/test/xlang_protocol_test.dart
new file mode 100644
index 000000000..1c34bc856
--- /dev/null
+++ b/dart/packages/fory/test/xlang_protocol_test.dart
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+import 'dart:typed_data';
+
+import 'package:fory/fory.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('xlang protocol regressions', () {
+    test('deserializes NONE wire values as null', () {
+      final fory = Fory();
+      final bytes = Uint8List.fromList(<int>[0x02, 0xff, TypeIds.none]);
+
+      expect(fory.deserialize<Object?>(bytes), isNull);
+      expect(fory.deserialize<Null>(bytes), isNull);
+    });
+
+    test('deserializes FLOAT16_ARRAY wire values', () {
+      final fory = Fory();
+      final bytes = Uint8List.fromList(
+        fory.serialize(
+          Uint16List.fromList(<int>[0x3c00, 0xc000, 0x7e00]),
+        ),
+      );
+      bytes[2] = TypeIds.float16Array;
+
+      final values = fory.deserialize<Float16List>(bytes);
+
+      expect(
+        values.map((value) => value.toBits()).toList(),
+        orderedEquals(<int>[0x3c00, 0xc000, 0x7e00]),
+      );
+    });
+
+    test('deserializes BFLOAT16 and BFLOAT16_ARRAY wire values', () {
+      final fory = Fory();
+      final scalarBytes = Uint8List.fromList(fory.serialize(Uint16(0xbf60)));
+      scalarBytes[2] = TypeIds.bfloat16;
+      final arrayBytes = Uint8List.fromList(
+        fory.serialize(
+          Uint16List.fromList(<int>[0x3f80, 0xbf80, 0x7fc1]),
+        ),
+      );
+      arrayBytes[2] = TypeIds.bfloat16Array;
+
+      expect(fory.deserialize<Bfloat16>(scalarBytes).toBits(), equals(0xbf60));
+      expect(
+        fory
+            .deserialize<Bfloat16List>(arrayBytes)
+            .map((value) => value.toBits())
+            .toList(),
+        orderedEquals(<int>[0x3f80, 0xbf80, 0x7fc1]),
+      );
+    });
+
+    test('rejects out-of-band xlang payload headers', () {
+      final fory = Fory();
+      final bytes = Uint8List.fromList(fory.serialize('value'));
+      bytes[0] |= 0x04;
+
+      expect(
+        () => fory.deserialize<String>(bytes),
+        throwsA(
+          isA<StateError>().having(
+            (error) => error.toString(),
+            'message',
+            contains('Out-of-band buffers'),
+          ),
+        ),
+      );
+    });
+  });
+}
diff --git a/docs/guide/dart/cross-language.md b/docs/guide/dart/cross-language.md
index fb8311692..a659c9257 100644
--- a/docs/guide/dart/cross-language.md
+++ b/docs/guide/dart/cross-language.md
@@ -160,7 +160,7 @@ Fory matches fields by name or by stable field ID. For robust cross-language int
 2. Assign stable `@ForyField(id: ...)` values to all fields before shipping the first payload.
 3. Keep field names consistent or rely on IDs, since Dart typically uses `lowerCamelCase` while Go uses `PascalCase` for exported fields and C# often uses `PascalCase` properties.
 4. Use compatible numeric types: `Int32` in Dart for Java `int`, Go `int32`, and C# `int`; `double` in Dart for 64-bit floats; `Float32` for 32-bit.
-5. Use `Timestamp` and `LocalDate` for date/time fields rather than raw `DateTime`.
+5. Use `Timestamp`, `LocalDate`, and `Duration` for temporal fields rather than raw `DateTime`.
 6. Validate real round trips across all languages before shipping.
 
 ## Type Mapping Notes for Dart
@@ -169,8 +169,9 @@ Because Dart `int` is not itself a promise about the exact xlang wire width, pre
 
 - `Int32` for xlang `int32`
 - `UInt32` for xlang `uint32`
-- `Float16` and `Float32` for reduced-width floating point
-- `Timestamp` and `LocalDate` for explicit temporal semantics
+- `Float16`, `Bfloat16`, and `Float32` for reduced-width floating point
+- `Float16List` and `Bfloat16List` for 16-bit floating-point array payloads
+- `Timestamp`, `LocalDate`, and `Duration` for explicit temporal semantics
 
 See [Supported Types](supported-types.md) and [xlang type mapping](../../specification/xlang_type_mapping.md).
 
diff --git a/docs/guide/dart/index.md b/docs/guide/dart/index.md
index b5517810e..0e814c447 100644
--- a/docs/guide/dart/index.md
+++ b/docs/guide/dart/index.md
@@ -114,9 +114,10 @@ dart run build_runner build --delete-conflicting-outputs
 - `fory.deserialize<T>(bytes)` — returns a `T`
 - `@ForyStruct()` — marks a class for code generation
 - `@ForyField(...)` — per-field options (skip, ID, nullability, references)
-- Integer wrappers: `Int8`, `Int16`, `Int32`, `UInt8`, `UInt16`, `UInt32`
-- Float wrappers: `Float16`, `Float32`
-- Time wrappers: `LocalDate`, `Timestamp`
+- Integer wrappers: `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
+- Float wrappers: `Float16`, `Bfloat16`, `Float32`
+- 16-bit float arrays: `Float16List`, `Bfloat16List`
+- Time types: `LocalDate`, `Timestamp`, `Duration`
 
 ## Documentation
 
diff --git a/docs/guide/dart/supported-types.md b/docs/guide/dart/supported-types.md
index a6acc5f2d..ad2b5c273 100644
--- a/docs/guide/dart/supported-types.md
+++ b/docs/guide/dart/supported-types.md
@@ -54,10 +54,16 @@ Each wrapper clamps the stored value to the target bit width.
 
 ## Floating-Point Wrappers
 
-Dart `double` maps to 64-bit float. If the peer uses a 32-bit float, use a wrapper:
+Dart `double` maps to 64-bit float. If the peer uses reduced-precision
+floating-point values, use an explicit wrapper:
 
 - `Float32` — 32-bit float (matches Java `float`, C# `float`, Go `float32`)
 - `Float16` — half-precision, for specialized numeric payloads
+- `Bfloat16` — brain floating point, useful when interoperating with ML-oriented payloads
+
+For contiguous 16-bit floating-point arrays, use `Float16List` and
+`Bfloat16List` rather than `Uint16List` when the wire type is `float16_array`
+or `bfloat16_array`.
 
 ## Time and Date Types
 
@@ -65,12 +71,18 @@ Avoid sending raw `DateTime` across languages — time zone handling and epoch d
 
 - `Timestamp` — a UTC instant with nanosecond precision (seconds + nanoseconds)
 - `LocalDate` — a calendar date without time or time zone
+- `Duration` — an elapsed time value using Dart's built-in `Duration`
 
 ```dart
 final now = Timestamp.fromDateTime(DateTime.now().toUtc());
 final birthday = LocalDate(1990, 12, 1);
+final timeout = const Duration(seconds: 30);
 ```
 
+`Duration` support in Dart is exact to microseconds. Incoming xlang duration
+payloads that use sub-microsecond nanoseconds are rejected instead of being
+silently truncated.
+
 ## Structs and Enums
 
 Annotate classes with `@ForyStruct()` and run `build_runner` to make them serializable. Enums in the same file are included automatically.
diff --git a/docs/specification/xlang_type_mapping.md b/docs/specification/xlang_type_mapping.md
index 7e458189f..748cb4db9 100644
--- a/docs/specification/xlang_type_mapping.md
+++ b/docs/specification/xlang_type_mapping.md
@@ -67,7 +67,7 @@ When reading type IDs:
 | tagged_uint64           | 15           | long/Long       | int/pyfory.tagged_uint64 | Type.taggedUInt64() | uint64_t                       | uint64           | u64               |
 | float8                  | 16           | /               | /                        | /                   | /                              | /                | /                 |
 | float16                 | 17           | Float16         | float/pyfory.float16     | Type.float16()      | fory::float16_t                | fory.float16     | fory::f16         |
-| bfloat16                | 18           | /               | /                        | /                   | /                              | /                | /                 |
+| bfloat16                | 18           | Bfloat16        | /                        | /                   | /                              | /                | /                 |
 | float32                 | 19           | float/Float     | float/pyfory.float32     | Type.float32()      | float                          | float32          | f32               |
 | float64                 | 20           | double/Double   | float/pyfory.float64     | Type.float64()      | double                         | float64          | f64               |
 | string                  | 21           | String          | str                      | String              | string                         | string           | String/str        |
@@ -101,7 +101,7 @@ When reading type IDs:
 | uint64_array            | 51           | long[]          | ndarray(uint64)          | /                   | `uint64_t[n]/vector<T>`        | `[n]uint64/[]T`  | `Vec<u64>`        |
 | float8_array            | 52           | /               | /                        | /                   | /                              | /                | /                 |
 | float16_array           | 53           | Float16List     | ndarray(float16)         | /                   | `fory::float16_t[n]/vector<T>` | `[n]float16/[]T` | `Vec<fory::f16>`  |
-| bfloat16_array          | 54           | /               | /                        | /                   | /                              | /                | /                 |
+| bfloat16_array          | 54           | Bfloat16List    | /                        | /                   | /                              | /                | /                 |
 | float32_array           | 55           | float[]         | ndarray(float32)         | /                   | `float[n]/vector<T>`           | `[n]float32/[]T` | `Vec<f32>`        |
 | float64_array           | 56           | double[]        | ndarray(float64)         | /                   | `double[n]/vector<T>`          | `[n]float64/[]T` | `Vec<f64>`        |
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org