From f824bba2125a04481374100a79af278b9f3c0c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=85=95=E7=99=BD?= Date: Mon, 4 May 2026 22:07:35 +0800 Subject: [PATCH] fix xlang type system --- benchmarks/python/README.md | 2 +- compiler/fory_compiler/ir/validator.py | 38 ++++- .../tests/test_xlang_type_system.py | 39 ++++- cpp/fory/serialization/temporal_serializers.h | 4 + cpp/fory/serialization/union_serializer.h | 2 +- dart/packages/fory/lib/src/fory.dart | 22 +-- ...calar_and_typed_array_serializer_test.dart | 15 ++ docs/compiler/schema-idl.md | 10 +- .../specification/xlang_serialization_spec.md | 3 +- go/fory/field_spec.go | 2 - go/fory/tag_test.go | 8 - integration_tests/idl_tests/cpp/main.cc | 159 +++++++++++++++--- .../csharp/IdlTests/RoundtripTests.cs | 95 ++++++++--- .../dart/test/idl_roundtrip_test.dart | 133 +++++++++++++-- .../idl_tests/go/idl_roundtrip_test.go | 28 +++ integration_tests/idl_tests/idl/example.fdl | 42 +++++ .../fory/idl_tests/IdlRoundTripTest.java | 114 ++++++------- .../idl_tests/javascript/roundtrip.ts | 36 +++- .../javascript/test/roundtrip.test.ts | 31 +++- .../idl_tests/python/idl_tests/roundtrip.py | 25 +++ .../idl_tests/rust/tests/idl_roundtrip.rs | 49 ++++-- .../IdlRoundTripTests/IdlRoundTripTests.swift | 44 ++++- .../kotlin/TypeUseAnnotationTest.kt | 45 +++++ rust/fory/src/lib.rs | 2 +- .../scala/TypeUseAnnotationTest.scala | 42 +++++ 25 files changed, 807 insertions(+), 183 deletions(-) create mode 100644 kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/TypeUseAnnotationTest.kt create mode 100644 scala/src/test/scala/org/apache/fory/serializer/scala/TypeUseAnnotationTest.scala diff --git a/benchmarks/python/README.md b/benchmarks/python/README.md index 5a968f8fc0..c2da3f8070 100644 --- a/benchmarks/python/README.md +++ b/benchmarks/python/README.md @@ -7,7 +7,7 @@ This directory contains two benchmark entrypoints: - `StructList`, `SampleList`, `MediaContentList` - operations: `serialize`, `deserialize` - serializers: `fory`, `pickle`, `protobuf` -2. `fory_benchmark.py`: CPython microbench script using the current 1.0 annotation surface. +2. `fory_benchmark.py`: CPython microbench script using the current annotation surface. ## Quick Start (Comprehensive Suite) diff --git a/compiler/fory_compiler/ir/validator.py b/compiler/fory_compiler/ir/validator.py index 439f65f35a..eccd99ce89 100644 --- a/compiler/fory_compiler/ir/validator.py +++ b/compiler/fory_compiler/ir/validator.py @@ -37,6 +37,18 @@ from fory_compiler.ir.types import ARRAY_ELEMENT_KINDS, PrimitiveKind from fory_compiler.ir.type_id import compute_registered_type_id +INVALID_MAP_KEY_KINDS = { + PrimitiveKind.BYTES, + PrimitiveKind.FLOAT16, + PrimitiveKind.BFLOAT16, + PrimitiveKind.FLOAT32, + PrimitiveKind.FLOAT64, + PrimitiveKind.DECIMAL, +} +INVALID_MAP_KEY_MESSAGE = ( + "map keys do not support binary, float, decimal, list, map, or array types" +) + @dataclass class ValidationIssue: @@ -69,7 +81,7 @@ def validate(self) -> bool: self._check_messages() self._check_type_references() self._check_services() - self._check_array_rules() + self._check_collection_type_rules() if not self.allow_nested_collections: self._check_collection_nesting() self._check_ref_rules() @@ -524,13 +536,17 @@ def check_message_fields( for f in union.fields: check_field(f, None) - def _check_array_rules(self) -> None: + def _check_collection_type_rules(self) -> None: + def invalid_map_key(field_type: FieldType) -> bool: + if isinstance(field_type, PrimitiveType): + return field_type.kind in INVALID_MAP_KEY_KINDS + return isinstance(field_type, (ListType, ArrayType, MapType)) + def check_type(field_type: FieldType, field: Field, in_map_key: bool = False): if isinstance(field_type, ArrayType): if in_map_key: - self._error( - "array is not valid as a map key type", field.location - ) + self._error(INVALID_MAP_KEY_MESSAGE, field.location) + return element_type = field_type.element_type if not isinstance(element_type, PrimitiveType): self._error( @@ -551,9 +567,19 @@ def check_type(field_type: FieldType, field: Field, in_map_key: bool = False): ) return if isinstance(field_type, ListType): + if in_map_key: + self._error(INVALID_MAP_KEY_MESSAGE, field.location) + return check_type(field_type.element_type, field) elif isinstance(field_type, MapType): - check_type(field_type.key_type, field, in_map_key=True) + if in_map_key: + self._error(INVALID_MAP_KEY_MESSAGE, field.location) + return + key_type = field_type.key_type + if invalid_map_key(key_type): + self._error(INVALID_MAP_KEY_MESSAGE, field.location) + else: + check_type(key_type, field, in_map_key=True) check_type(field_type.value_type, field) def check_message_fields(message: Message) -> None: diff --git a/compiler/fory_compiler/tests/test_xlang_type_system.py b/compiler/fory_compiler/tests/test_xlang_type_system.py index 49a4be7894..b03be36991 100644 --- a/compiler/fory_compiler/tests/test_xlang_type_system.py +++ b/compiler/fory_compiler/tests/test_xlang_type_system.py @@ -108,12 +108,24 @@ def test_array_type_is_distinct_from_list_type(): "any", "date", "timestamp", + "duration", "decimal", + "ExampleState", "Child", + "ChildUnion", ], ) def test_array_rejects_non_fixed_width_number_and_bool_elements(element): source = f""" + enum ExampleState {{ + UNKNOWN = 0; + READY = 1; + }} + + union ChildUnion {{ + string note = 1; + }} + message Child {{ string name = 1; }} @@ -151,18 +163,33 @@ def test_array_rejects_optional_or_ref_elements_at_parse_time(): ) -def test_array_is_not_valid_as_map_key(): +@pytest.mark.parametrize( + "key_type", + [ + "bytes", + "float16", + "bfloat16", + "float32", + "float64", + "decimal", + "list", + "array", + "map", + ], +) +def test_map_rejects_non_portable_key_types(key_type): _schema, validator, ok = validate_schema( - """ - message InvalidMap { - map, string> values = 1; - } + f""" + message InvalidMap {{ + map<{key_type}, string> values = 1; + }} """ ) assert not ok assert any( - "array is not valid as a map key type" in err.message + "map keys do not support binary, float, decimal, list, map, or array types" + in err.message for err in validator.errors ) diff --git a/cpp/fory/serialization/temporal_serializers.h b/cpp/fory/serialization/temporal_serializers.h index 86737059ed..978809c6bf 100644 --- a/cpp/fory/serialization/temporal_serializers.h +++ b/cpp/fory/serialization/temporal_serializers.h @@ -51,6 +51,10 @@ class Date { bool operator!=(const Date &other) const { return !(*this == other); } + bool operator<(const Date &other) const { + return days_since_epoch_ < other.days_since_epoch_; + } + private: int32_t days_since_epoch_; // Days since Jan 1, 1970 UTC }; diff --git a/cpp/fory/serialization/union_serializer.h b/cpp/fory/serialization/union_serializer.h index 2367fde88b..2fda11fac6 100644 --- a/cpp/fory/serialization/union_serializer.h +++ b/cpp/fory/serialization/union_serializer.h @@ -40,7 +40,7 @@ namespace fory { namespace serialization { -// Union metadata specializations generated by compiler (legacy). +// Compiler-generated union metadata specializations. template struct UnionCaseIds; template struct UnionCaseMeta; diff --git a/dart/packages/fory/lib/src/fory.dart b/dart/packages/fory/lib/src/fory.dart index f082a4eabc..80d8190486 100644 --- a/dart/packages/fory/lib/src/fory.dart +++ b/dart/packages/fory/lib/src/fory.dart @@ -43,7 +43,8 @@ final class Fory { static const int _xlangHeaderFlag = 0x02; static const int _outOfBandHeaderFlag = 0x04; - late final Buffer _buffer; + late final Buffer _readBuffer; + late final Buffer _writeBuffer; late final WriteContext _writeContext; late final ReadContext _readContext; late final TypeResolver _typeResolver; @@ -66,7 +67,8 @@ final class Fory { maxCollectionSize: maxCollectionSize, maxBinarySize: maxBinarySize, ); - _buffer = Buffer(); + _readBuffer = Buffer(); + _writeBuffer = Buffer(); _typeResolver = TypeResolver(config); _writeContext = WriteContext( config, @@ -88,9 +90,9 @@ final class Fory { /// that needs shared-reference tracking and there is no field metadata to /// request it. Annotated fields should still use `@ForyField(ref: true)`. Uint8List serialize(Object? value, {bool trackRef = false}) { - _buffer.clear(); - serializeTo(value, _buffer, trackRef: trackRef); - return Uint8List.fromList(_buffer.toBytes()); + _writeBuffer.clear(); + serializeTo(value, _writeBuffer, trackRef: trackRef); + return Uint8List.fromList(_writeBuffer.toBytes()); } /// Serializes a non-null builtin [value] using the explicit xlang @@ -104,14 +106,14 @@ final class Fory { required int wireTypeId, bool trackRef = false, }) { - _buffer.clear(); + _writeBuffer.clear(); serializeBuiltinTo( value, - _buffer, + _writeBuffer, wireTypeId: wireTypeId, trackRef: trackRef, ); - return Uint8List.fromList(_buffer.toBytes()); + return Uint8List.fromList(_writeBuffer.toBytes()); } /// Serializes [value] into [buffer]. @@ -163,8 +165,8 @@ final class Fory { /// The payload is decoded from its wire metadata first. `T` is used as a /// post-read type check, not as an alternate schema. T deserialize(Uint8List bytes) { - _buffer.wrap(bytes); - return deserializeFrom(_buffer); + _readBuffer.wrap(bytes); + return deserializeFrom(_readBuffer); } /// Deserializes a value from [buffer] and checks that it is assignable to 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 index c58b902a1c..5d61240aba 100644 --- a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart +++ b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart @@ -389,6 +389,21 @@ void main() { ); }); + test('reuses Fory after typed-array reads without corrupting views', () { + final fory = Fory(); + final values = [ + Int32List.fromList([1, 2]), + Int32List.fromList([3, 4]), + ]; + + final decoded = fory.deserialize>(fory.serialize(values)); + final encodedAgain = fory.serialize(decoded); + final roundTrip = fory.deserialize>(encodedAgain); + + expect((roundTrip[0] as Int32List).toList(), orderedEquals([1, 2])); + expect((roundTrip[1] as Int32List).toList(), orderedEquals([3, 4])); + }); + test('round-trips empty binary and typed array payloads', () { final fory = Fory(); diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md index 68bcf70d8d..63a6e3ef3b 100644 --- a/docs/compiler/schema-idl.md +++ b/docs/compiler/schema-idl.md @@ -1274,7 +1274,7 @@ Generated carriers are language-specific, but the schema kind is not: | ----------------- | ---------------------------- | ---------------------- | -------------- | ------------------------ | | `list` | `BoolList` / `List` | `List[bool]` | `List` | `Type.list(Type.bool())` | | `array` | `boolean[]` | `pyfory.BoolArray` | `BoolList` | `Type.boolArray()` | -| `array` | `byte[]` | `pyfory.Int8Array` | `Int8List` | `Type.int8Array()` | +| `array` | `@Int8Type byte[]` | `pyfory.Int8Array` | `Int8List` | `Type.int8Array()` | | `array` | `short[]` | `pyfory.Int16Array` | `Int16List` | `Type.int16Array()` | | `array` | `int[]` | `pyfory.Int32Array` | `Int32List` | `Type.int32Array()` | | `array` | `long[]` | `pyfory.Int64Array` | `Int64List` | `Type.int64Array()` | @@ -1316,10 +1316,14 @@ message Config { **Key Type Restrictions:** - `string` (most common) -- Integer types (`int8`, `int16`, `int32`, `int64`) - `bool` +- Integer types (`int8`, `int16`, `int32`, `int64`, `uint8`, `uint16`, `uint32`, `uint64`) +- Temporal scalar types (`date`, `timestamp`, `duration`) +- Enums -Avoid using messages or complex types as keys. +Map keys do not support binary `bytes`, floating-point types, `decimal`, `list`, `array`, +or nested `map` types. Put those types in map values or wrap them in a message with a +portable scalar or enum key. ### Type Compatibility Matrix diff --git a/docs/specification/xlang_serialization_spec.md b/docs/specification/xlang_serialization_spec.md index 37a1968012..24adbe3331 100644 --- a/docs/specification/xlang_serialization_spec.md +++ b/docs/specification/xlang_serialization_spec.md @@ -63,7 +63,8 @@ This specification defines the Fory xlang binary format. The format is dynamic r - named_ext: an `ext` type whose type mapping will be encoded as a name. - list: a sequence of objects. - set: an unordered set of unique elements. -- map: a map of key-value pairs. Mutable types such as `list/map/set/array` are not allowed as key of map. +- map: a map of key-value pairs. Map keys do not allow binary values, floating-point values, + decimal values, or collection-shaped values such as `list`, `map`, `set`, and `array`. - duration: an absolute length of time, independent of any calendar/timezone, as a count of nanoseconds. - timestamp: a point in time, independent of any calendar/timezone, encoded as seconds (int64) and nanoseconds (uint32) since the epoch at UTC midnight on January 1, 1970. diff --git a/go/fory/field_spec.go b/go/fory/field_spec.go index cd7e59b202..68f15a69b7 100644 --- a/go/fory/field_spec.go +++ b/go/fory/field_spec.go @@ -558,8 +558,6 @@ func parseFieldTag(field reflect.StructField) (parsedFieldTag, error) { } parsed.typeHintSet = true parsed.typeHint = hint - case "compress", "nested_ref": - return parsedFieldTag{}, InvalidTagErrorf("unsupported legacy fory tag key %q on field %s", key, field.Name) default: return parsedFieldTag{}, InvalidTagErrorf("unknown fory tag key %q on field %s", key, field.Name) } diff --git a/go/fory/tag_test.go b/go/fory/tag_test.go index aeeb551dda..11382d6f7d 100644 --- a/go/fory/tag_test.go +++ b/go/fory/tag_test.go @@ -220,12 +220,6 @@ func TestParseFieldSpecRejectsInvalidTags(t *testing.T) { type UnknownKey struct { Value int32 `fory:"id=0,unknown=true"` } - type LegacyCompress struct { - Value uint32 `fory:"compress=true"` - } - type LegacyNestedRef struct { - Value []int32 `fory:"nested_ref=[[]]"` - } type BadDSL struct { Value []int32 `fory:"type=list(element=int32(encoding=fixed)"` } @@ -254,8 +248,6 @@ func TestParseFieldSpecRejectsInvalidTags(t *testing.T) { }{ {name: "duplicate keys", typ: reflect.TypeOf(DuplicateKeys{})}, {name: "unknown key", typ: reflect.TypeOf(UnknownKey{})}, - {name: "legacy compress", typ: reflect.TypeOf(LegacyCompress{})}, - {name: "legacy nested_ref", typ: reflect.TypeOf(LegacyNestedRef{})}, {name: "bad dsl", typ: reflect.TypeOf(BadDSL{})}, {name: "impossible override", typ: reflect.TypeOf(ImpossibleOverride{})}, {name: "encoding conflict", typ: reflect.TypeOf(Conflict{})}, diff --git a/integration_tests/idl_tests/cpp/main.cc b/integration_tests/idl_tests/cpp/main.cc index b97bd857dc..8e41bd1d54 100644 --- a/integration_tests/idl_tests/cpp/main.cc +++ b/integration_tests/idl_tests/cpp/main.cc @@ -433,7 +433,7 @@ struct ExampleMessageLists { fory::F(132).list(fory::T::inner(fory::T::uint64().varint())))); }; -struct ExampleMessageArraysMaps { +struct ExampleMessageArrays { std::vector bool_array; std::vector int8_array; std::vector int16_array; @@ -449,6 +449,28 @@ struct ExampleMessageArraysMaps { std::vector float64_array; std::vector> int32_array_list; std::vector> uint8_array_list; + + FORY_STRUCT(ExampleMessageArrays, + (bool_array, fory::F(301).array(fory::T::boolean())), + (int8_array, fory::F(302).array(fory::T::int8())), + (int16_array, fory::F(303).array(fory::T::int16())), + (int32_array, fory::F(304).array(fory::T::int32())), + (int64_array, fory::F(305).array(fory::T::int64())), + (uint8_array, fory::F(306).array(fory::T::uint8())), + (uint16_array, fory::F(307).array(fory::T::uint16())), + (uint32_array, fory::F(308).array(fory::T::uint32())), + (uint64_array, fory::F(309).array(fory::T::uint64())), + (float16_array, fory::F(310).array(fory::T::float16())), + (bfloat16_array, fory::F(311).array(fory::T::bfloat16())), + (float32_array, fory::F(312).array(fory::T::float32())), + (float64_array, fory::F(313).array(fory::T::float64())), + (int32_array_list, + fory::F(314).list(fory::T::array(fory::T::int32()))), + (uint8_array_list, + fory::F(315).list(fory::T::array(fory::T::uint8())))); +}; + +struct ExampleMessageMaps { std::map string_values_by_bool; std::map string_values_by_int8; std::map string_values_by_int16; @@ -482,24 +504,31 @@ struct ExampleMessageArraysMaps { std::map> uint8_array_values_by_name; std::map> float32_array_values_by_name; std::map> int32_array_values_by_name; + std::map string_values_by_date; + std::map bool_values_by_name; + std::map int8_values_by_name; + std::map int16_values_by_name; + std::map fixed_i32_values_by_name; + std::map varint_i32_values_by_name; + std::map fixed_i64_values_by_name; + std::map varint_i64_values_by_name; + std::map tagged_i64_values_by_name; + std::map uint8_values_by_name; + std::map uint16_values_by_name; + std::map fixed_u32_values_by_name; + std::map varint_u32_values_by_name; + std::map fixed_u64_values_by_name; + std::map varint_u64_values_by_name; + std::map tagged_u64_values_by_name; + std::map float32_values_by_name; + std::map float64_values_by_name; + std::map + timestamp_values_by_name; + std::map duration_values_by_name; + std::map enum_values_by_name; FORY_STRUCT( - ExampleMessageArraysMaps, - (bool_array, fory::F(301).array(fory::T::boolean())), - (int8_array, fory::F(302).array(fory::T::int8())), - (int16_array, fory::F(303).array(fory::T::int16())), - (int32_array, fory::F(304).array(fory::T::int32())), - (int64_array, fory::F(305).array(fory::T::int64())), - (uint8_array, fory::F(306).array(fory::T::uint8())), - (uint16_array, fory::F(307).array(fory::T::uint16())), - (uint32_array, fory::F(308).array(fory::T::uint32())), - (uint64_array, fory::F(309).array(fory::T::uint64())), - (float16_array, fory::F(310).array(fory::T::float16())), - (bfloat16_array, fory::F(311).array(fory::T::bfloat16())), - (float32_array, fory::F(312).array(fory::T::float32())), - (float64_array, fory::F(313).array(fory::T::float64())), - (int32_array_list, fory::F(314).list(fory::T::array(fory::T::int32()))), - (uint8_array_list, fory::F(315).list(fory::T::array(fory::T::uint8()))), + ExampleMessageMaps, (string_values_by_bool, fory::F(201).map(fory::T::boolean(), fory::T::string())), (string_values_by_int8, @@ -562,12 +591,55 @@ struct ExampleMessageArraysMaps { (float32_array_values_by_name, fory::F(232).map(fory::T::string(), fory::T::array(fory::T::float32()))), (int32_array_values_by_name, - fory::F(233).map(fory::T::string(), fory::T::array(fory::T::int32())))); + fory::F(233).map(fory::T::string(), fory::T::array(fory::T::int32()))), + (string_values_by_date, + fory::F(234).map(fory::FieldNodeSpec{}, fory::T::string())), + (bool_values_by_name, + fory::F(236).map(fory::T::string(), fory::T::boolean())), + (int8_values_by_name, + fory::F(237).map(fory::T::string(), fory::T::int8())), + (int16_values_by_name, + fory::F(238).map(fory::T::string(), fory::T::int16())), + (fixed_i32_values_by_name, + fory::F(239).map(fory::T::string(), fory::T::int32().fixed())), + (varint_i32_values_by_name, + fory::F(240).map(fory::T::string(), fory::T::int32().varint())), + (fixed_i64_values_by_name, + fory::F(241).map(fory::T::string(), fory::T::int64().fixed())), + (varint_i64_values_by_name, + fory::F(242).map(fory::T::string(), fory::T::int64().varint())), + (tagged_i64_values_by_name, + fory::F(243).map(fory::T::string(), fory::T::int64().tagged())), + (uint8_values_by_name, + fory::F(244).map(fory::T::string(), fory::T::uint8())), + (uint16_values_by_name, + fory::F(245).map(fory::T::string(), fory::T::uint16())), + (fixed_u32_values_by_name, + fory::F(246).map(fory::T::string(), fory::T::uint32().fixed())), + (varint_u32_values_by_name, + fory::F(247).map(fory::T::string(), fory::T::uint32().varint())), + (fixed_u64_values_by_name, + fory::F(248).map(fory::T::string(), fory::T::uint64().fixed())), + (varint_u64_values_by_name, + fory::F(249).map(fory::T::string(), fory::T::uint64().varint())), + (tagged_u64_values_by_name, + fory::F(250).map(fory::T::string(), fory::T::uint64().tagged())), + (float32_values_by_name, + fory::F(251).map(fory::T::string(), fory::T::float32())), + (float64_values_by_name, + fory::F(252).map(fory::T::string(), fory::T::float64())), + (timestamp_values_by_name, + fory::F(253).map(fory::T::string(), fory::FieldNodeSpec{})), + (duration_values_by_name, + fory::F(254).map(fory::T::string(), fory::FieldNodeSpec{})), + (enum_values_by_name, + fory::F(255).map(fory::T::string(), fory::FieldNodeSpec{}))); }; struct ExampleMessage : ExampleMessageScalars, ExampleMessageLists, - ExampleMessageArraysMaps { + ExampleMessageArrays, + ExampleMessageMaps { bool operator==(const ExampleMessage &other) const { return std::tie( @@ -605,7 +677,17 @@ struct ExampleMessage : ExampleMessageScalars, date_values_by_name, decimal_values_by_name, message_values_by_name, union_values_by_name, uint8_array_values_by_name, float32_array_values_by_name, - int32_array_values_by_name) == + int32_array_values_by_name, string_values_by_date, + bool_values_by_name, int8_values_by_name, int16_values_by_name, + fixed_i32_values_by_name, varint_i32_values_by_name, + fixed_i64_values_by_name, varint_i64_values_by_name, + tagged_i64_values_by_name, uint8_values_by_name, + uint16_values_by_name, fixed_u32_values_by_name, + varint_u32_values_by_name, fixed_u64_values_by_name, + varint_u64_values_by_name, tagged_u64_values_by_name, + float32_values_by_name, float64_values_by_name, + timestamp_values_by_name, duration_values_by_name, + enum_values_by_name) == std::tie( other.bool_value, other.int8_value, other.int16_value, other.fixed_i32_value, other.varint_i32_value, @@ -656,12 +738,22 @@ struct ExampleMessage : ExampleMessageScalars, other.message_values_by_name, other.union_values_by_name, other.uint8_array_values_by_name, other.float32_array_values_by_name, - other.int32_array_values_by_name); + other.int32_array_values_by_name, other.string_values_by_date, + other.bool_values_by_name, other.int8_values_by_name, + other.int16_values_by_name, other.fixed_i32_values_by_name, + other.varint_i32_values_by_name, other.fixed_i64_values_by_name, + other.varint_i64_values_by_name, other.tagged_i64_values_by_name, + other.uint8_values_by_name, other.uint16_values_by_name, + other.fixed_u32_values_by_name, other.varint_u32_values_by_name, + other.fixed_u64_values_by_name, other.varint_u64_values_by_name, + other.tagged_u64_values_by_name, other.float32_values_by_name, + other.float64_values_by_name, other.timestamp_values_by_name, + other.duration_values_by_name, other.enum_values_by_name); } FORY_STRUCT(ExampleMessage, FORY_BASE(ExampleMessageScalars), - FORY_BASE(ExampleMessageLists), - FORY_BASE(ExampleMessageArraysMaps)); + FORY_BASE(ExampleMessageLists), FORY_BASE(ExampleMessageArrays), + FORY_BASE(ExampleMessageMaps)); }; FORY_ENUM(ExampleState, UNKNOWN, READY, FAILED); @@ -835,6 +927,27 @@ example_peer::ExampleMessage BuildExampleMessage() { {"u8", {static_cast(201), static_cast(202)}}}; message.float32_array_values_by_name = {{"f32", {1.25F, 2.5F}}}; message.int32_array_values_by_name = {{"i32", {101, 202}}}; + message.string_values_by_date = {{Date(19850), "date-key"}}; + message.bool_values_by_name = {{"bool", true}}; + message.int8_values_by_name = {{"int8", -8}}; + message.int16_values_by_name = {{"int16", -16}}; + message.fixed_i32_values_by_name = {{"fixed-i32", -32}}; + message.varint_i32_values_by_name = {{"varint-i32", 32}}; + message.fixed_i64_values_by_name = {{"fixed-i64", -64}}; + message.varint_i64_values_by_name = {{"varint-i64", 64}}; + message.tagged_i64_values_by_name = {{"tagged-i64", 65}}; + message.uint8_values_by_name = {{"uint8", static_cast(208)}}; + message.uint16_values_by_name = {{"uint16", 60001}}; + message.fixed_u32_values_by_name = {{"fixed-u32", 1234567892U}}; + message.varint_u32_values_by_name = {{"varint-u32", 1234567893U}}; + message.fixed_u64_values_by_name = {{"fixed-u64", 9876543213ULL}}; + message.varint_u64_values_by_name = {{"varint-u64", 9876543214ULL}}; + message.tagged_u64_values_by_name = {{"tagged-u64", 9876543215ULL}}; + message.float32_values_by_name = {{"float32", 3.25F}}; + message.float64_values_by_name = {{"float64", 6.5}}; + message.timestamp_values_by_name = {{"timestamp", ts(1717747750)}}; + message.duration_values_by_name = {{"duration", std::chrono::seconds(10)}}; + message.enum_values_by_name = {{"enum", example_peer::ExampleState::FAILED}}; return message; } diff --git a/integration_tests/idl_tests/csharp/IdlTests/RoundtripTests.cs b/integration_tests/idl_tests/csharp/IdlTests/RoundtripTests.cs index 4dbc99c880..92b5bb089f 100644 --- a/integration_tests/idl_tests/csharp/IdlTests/RoundtripTests.cs +++ b/integration_tests/idl_tests/csharp/IdlTests/RoundtripTests.cs @@ -695,6 +695,36 @@ private static example.ExampleMessage BuildExampleMessage() { ["i32"] = [101, 202], }, + StringValuesByDate = new Dictionary + { + [new DateOnly(2024, 5, 7)] = "date-key", + }, + BoolValuesByName = new Dictionary { ["bool"] = true }, + Int8ValuesByName = new Dictionary { ["int8"] = -8 }, + Int16ValuesByName = new Dictionary { ["int16"] = -16 }, + FixedI32ValuesByName = new Dictionary { ["fixed-i32"] = -32 }, + VarintI32ValuesByName = new Dictionary { ["varint-i32"] = 32 }, + FixedI64ValuesByName = new Dictionary { ["fixed-i64"] = -64 }, + VarintI64ValuesByName = new Dictionary { ["varint-i64"] = 64 }, + TaggedI64ValuesByName = new Dictionary { ["tagged-i64"] = 65 }, + Uint8ValuesByName = new Dictionary { ["uint8"] = 208 }, + Uint16ValuesByName = new Dictionary { ["uint16"] = 60001 }, + FixedU32ValuesByName = new Dictionary { ["fixed-u32"] = 1234567892 }, + VarintU32ValuesByName = new Dictionary { ["varint-u32"] = 1234567893 }, + FixedU64ValuesByName = new Dictionary { ["fixed-u64"] = 9876543213 }, + VarintU64ValuesByName = new Dictionary { ["varint-u64"] = 9876543214 }, + TaggedU64ValuesByName = new Dictionary { ["tagged-u64"] = 9876543215 }, + Float32ValuesByName = new Dictionary { ["float32"] = 3.25f }, + Float64ValuesByName = new Dictionary { ["float64"] = 6.5 }, + TimestampValuesByName = new Dictionary + { + ["timestamp"] = DateTimeOffset.Parse( + "2024-06-07T08:09:10Z", + CultureInfo.InvariantCulture, + DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal), + }, + DurationValuesByName = new Dictionary { ["duration"] = TimeSpan.FromSeconds(10) }, + EnumValuesByName = new Dictionary { ["enum"] = example.ExampleState.Failed }, }; } @@ -1106,6 +1136,27 @@ private static void AssertExampleMessage(example.ExampleMessage expected, exampl AssertArrayMap(expected.Uint8ArrayValuesByName, actual.Uint8ArrayValuesByName); AssertArrayMap(expected.Float32ArrayValuesByName, actual.Float32ArrayValuesByName); AssertArrayMap(expected.Int32ArrayValuesByName, actual.Int32ArrayValuesByName); + AssertMap(expected.StringValuesByDate, actual.StringValuesByDate); + AssertMap(expected.BoolValuesByName, actual.BoolValuesByName); + AssertMap(expected.Int8ValuesByName, actual.Int8ValuesByName); + AssertMap(expected.Int16ValuesByName, actual.Int16ValuesByName); + AssertMap(expected.FixedI32ValuesByName, actual.FixedI32ValuesByName); + AssertMap(expected.VarintI32ValuesByName, actual.VarintI32ValuesByName); + AssertMap(expected.FixedI64ValuesByName, actual.FixedI64ValuesByName); + AssertMap(expected.VarintI64ValuesByName, actual.VarintI64ValuesByName); + AssertMap(expected.TaggedI64ValuesByName, actual.TaggedI64ValuesByName); + AssertMap(expected.Uint8ValuesByName, actual.Uint8ValuesByName); + AssertMap(expected.Uint16ValuesByName, actual.Uint16ValuesByName); + AssertMap(expected.FixedU32ValuesByName, actual.FixedU32ValuesByName); + AssertMap(expected.VarintU32ValuesByName, actual.VarintU32ValuesByName); + AssertMap(expected.FixedU64ValuesByName, actual.FixedU64ValuesByName); + AssertMap(expected.VarintU64ValuesByName, actual.VarintU64ValuesByName); + AssertMap(expected.TaggedU64ValuesByName, actual.TaggedU64ValuesByName); + AssertMap(expected.Float32ValuesByName, actual.Float32ValuesByName); + AssertMap(expected.Float64ValuesByName, actual.Float64ValuesByName); + AssertMap(expected.TimestampValuesByName, actual.TimestampValuesByName); + AssertMap(expected.DurationValuesByName, actual.DurationValuesByName); + AssertMap(expected.EnumValuesByName, actual.EnumValuesByName); } private static void AssertExampleLeaf(example.ExampleLeaf? expected, example.ExampleLeaf? actual) @@ -1491,22 +1542,22 @@ private static bool TryStringList(object? value, out List result) result = [.. strEnumerable]; return true; case IEnumerable objEnumerable: - { - List normalized = []; - foreach (object? item in objEnumerable) { - if (item is not string text) + List normalized = []; + foreach (object? item in objEnumerable) { - result = []; - return false; + if (item is not string text) + { + result = []; + return false; + } + + normalized.Add(text); } - normalized.Add(text); + result = normalized; + return true; } - - result = normalized; - return true; - } default: result = []; return false; @@ -1524,22 +1575,22 @@ private static bool TryStringMap(object? value, out Dictionary r result = readonlyMap.ToDictionary(kv => kv.Key, kv => kv.Value); return true; case IEnumerable> objectPairs: - { - Dictionary normalized = []; - foreach (KeyValuePair pair in objectPairs) { - if (pair.Key is not string key || pair.Value is not string val) + Dictionary normalized = []; + foreach (KeyValuePair pair in objectPairs) { - result = []; - return false; + if (pair.Key is not string key || pair.Value is not string val) + { + result = []; + return false; + } + + normalized[key] = val; } - normalized[key] = val; + result = normalized; + return true; } - - result = normalized; - return true; - } default: result = []; return false; diff --git a/integration_tests/idl_tests/dart/test/idl_roundtrip_test.dart b/integration_tests/idl_tests/dart/test/idl_roundtrip_test.dart index e4d97cea70..c586603627 100644 --- a/integration_tests/idl_tests/dart/test/idl_roundtrip_test.dart +++ b/integration_tests/idl_tests/dart/test/idl_roundtrip_test.dart @@ -373,8 +373,10 @@ example.ExampleMessage buildExampleMessage() { ..uint32Array = Uint32List.fromList([2000000000, 2100000000]) ..uint64Array = Uint64List.fromList([9000000000, 12000000000]) ..float16Array = Float16List.fromList([Float16(1.0), Float16(2.0)]) - ..bfloat16Array = - Bfloat16List.fromList([Bfloat16(1.0), Bfloat16(2.0)]) + ..bfloat16Array = Bfloat16List.fromList([ + Bfloat16(1.0), + Bfloat16(2.0), + ]) ..float32Array = Float32List.fromList([1.5, 2.5]) ..float64Array = Float64List.fromList([3.5, 4.5]) ..int32ArrayList = [ @@ -399,10 +401,10 @@ example.ExampleMessage buildExampleMessage() { ..stringValuesByVarintU32 = {1234567891: 'varint-u32'} ..stringValuesByFixedU64 = {Uint64(9876543210): 'fixed-u64'} ..stringValuesByVarintU64 = { - Uint64(9876543211): 'varint-u64' + Uint64(9876543211): 'varint-u64', } ..stringValuesByTaggedU64 = { - Uint64(9876543212): 'tagged-u64' + Uint64(9876543212): 'tagged-u64', } ..stringValuesByString = {'name': 'value'} ..stringValuesByTimestamp = { @@ -441,6 +443,35 @@ example.ExampleMessage buildExampleMessage() { } ..int32ArrayValuesByName = { 'i32': Int32List.fromList([101, 202]), + } + ..stringValuesByDate = { + const LocalDate(2024, 5, 7): 'date-key', + } + ..boolValuesByName = {'bool': true} + ..int8ValuesByName = {'int8': -8} + ..int16ValuesByName = {'int16': -16} + ..fixedI32ValuesByName = {'fixed-i32': -32} + ..varintI32ValuesByName = {'varint-i32': 32} + ..fixedI64ValuesByName = {'fixed-i64': Int64(-64)} + ..varintI64ValuesByName = {'varint-i64': Int64(64)} + ..taggedI64ValuesByName = {'tagged-i64': Int64(65)} + ..uint8ValuesByName = {'uint8': 208} + ..uint16ValuesByName = {'uint16': 60001} + ..fixedU32ValuesByName = {'fixed-u32': 1234567892} + ..varintU32ValuesByName = {'varint-u32': 1234567893} + ..fixedU64ValuesByName = {'fixed-u64': Uint64(9876543213)} + ..varintU64ValuesByName = {'varint-u64': Uint64(9876543214)} + ..taggedU64ValuesByName = {'tagged-u64': Uint64(9876543215)} + ..float32ValuesByName = {'float32': Float32(3.25)} + ..float64ValuesByName = {'float64': 6.5} + ..timestampValuesByName = { + 'timestamp': Timestamp.fromDateTime(DateTime.utc(2024, 6, 7, 8, 9, 10)), + } + ..durationValuesByName = { + 'duration': const Duration(seconds: 10), + } + ..enumValuesByName = { + 'enum': example.ExampleState.failed, }; } @@ -1049,32 +1080,56 @@ void _expectExampleMessageEquals( _expectMapEquals(actual.stringValuesByInt8, expected.stringValuesByInt8); _expectMapEquals(actual.stringValuesByInt16, expected.stringValuesByInt16); _expectMapEquals( - actual.stringValuesByFixedI32, expected.stringValuesByFixedI32); + actual.stringValuesByFixedI32, + expected.stringValuesByFixedI32, + ); _expectMapEquals( - actual.stringValuesByVarintI32, expected.stringValuesByVarintI32); + actual.stringValuesByVarintI32, + expected.stringValuesByVarintI32, + ); _expectMapEquals( - actual.stringValuesByFixedI64, expected.stringValuesByFixedI64); + actual.stringValuesByFixedI64, + expected.stringValuesByFixedI64, + ); _expectMapEquals( - actual.stringValuesByVarintI64, expected.stringValuesByVarintI64); + actual.stringValuesByVarintI64, + expected.stringValuesByVarintI64, + ); _expectMapEquals( - actual.stringValuesByTaggedI64, expected.stringValuesByTaggedI64); + actual.stringValuesByTaggedI64, + expected.stringValuesByTaggedI64, + ); _expectMapEquals(actual.stringValuesByUint8, expected.stringValuesByUint8); _expectMapEquals(actual.stringValuesByUint16, expected.stringValuesByUint16); _expectMapEquals( - actual.stringValuesByFixedU32, expected.stringValuesByFixedU32); + actual.stringValuesByFixedU32, + expected.stringValuesByFixedU32, + ); _expectMapEquals( - actual.stringValuesByVarintU32, expected.stringValuesByVarintU32); + actual.stringValuesByVarintU32, + expected.stringValuesByVarintU32, + ); _expectMapEquals( - actual.stringValuesByFixedU64, expected.stringValuesByFixedU64); + actual.stringValuesByFixedU64, + expected.stringValuesByFixedU64, + ); _expectMapEquals( - actual.stringValuesByVarintU64, expected.stringValuesByVarintU64); + actual.stringValuesByVarintU64, + expected.stringValuesByVarintU64, + ); _expectMapEquals( - actual.stringValuesByTaggedU64, expected.stringValuesByTaggedU64); + actual.stringValuesByTaggedU64, + expected.stringValuesByTaggedU64, + ); _expectMapEquals(actual.stringValuesByString, expected.stringValuesByString); _expectMapEquals( - actual.stringValuesByTimestamp, expected.stringValuesByTimestamp); + actual.stringValuesByTimestamp, + expected.stringValuesByTimestamp, + ); _expectMapEquals( - actual.stringValuesByDuration, expected.stringValuesByDuration); + actual.stringValuesByDuration, + expected.stringValuesByDuration, + ); _expectMapEquals(actual.stringValuesByEnum, expected.stringValuesByEnum); _expectMapEquals(actual.float16ValuesByName, expected.float16ValuesByName); _expectMapEquals( @@ -1093,7 +1148,9 @@ void _expectExampleMessageEquals( _expectMapEquals(actual.dateValuesByName, expected.dateValuesByName); _expectMapEquals(actual.decimalValuesByName, expected.decimalValuesByName); expect( - actual.messageValuesByName.length, expected.messageValuesByName.length); + actual.messageValuesByName.length, + expected.messageValuesByName.length, + ); for (final entry in expected.messageValuesByName.entries) { final actualValue = actual.messageValuesByName[entry.key]; expect(actualValue, isNotNull); @@ -1117,6 +1174,48 @@ void _expectExampleMessageEquals( actual.int32ArrayValuesByName, expected.int32ArrayValuesByName, ); + _expectMapEquals(actual.stringValuesByDate, expected.stringValuesByDate); + _expectMapEquals(actual.boolValuesByName, expected.boolValuesByName); + _expectMapEquals(actual.int8ValuesByName, expected.int8ValuesByName); + _expectMapEquals(actual.int16ValuesByName, expected.int16ValuesByName); + _expectMapEquals(actual.fixedI32ValuesByName, expected.fixedI32ValuesByName); + _expectMapEquals( + actual.varintI32ValuesByName, + expected.varintI32ValuesByName, + ); + _expectMapEquals(actual.fixedI64ValuesByName, expected.fixedI64ValuesByName); + _expectMapEquals( + actual.varintI64ValuesByName, + expected.varintI64ValuesByName, + ); + _expectMapEquals( + actual.taggedI64ValuesByName, + expected.taggedI64ValuesByName, + ); + _expectMapEquals(actual.uint8ValuesByName, expected.uint8ValuesByName); + _expectMapEquals(actual.uint16ValuesByName, expected.uint16ValuesByName); + _expectMapEquals(actual.fixedU32ValuesByName, expected.fixedU32ValuesByName); + _expectMapEquals( + actual.varintU32ValuesByName, + expected.varintU32ValuesByName, + ); + _expectMapEquals(actual.fixedU64ValuesByName, expected.fixedU64ValuesByName); + _expectMapEquals( + actual.varintU64ValuesByName, + expected.varintU64ValuesByName, + ); + _expectMapEquals( + actual.taggedU64ValuesByName, + expected.taggedU64ValuesByName, + ); + _expectMapEquals(actual.float32ValuesByName, expected.float32ValuesByName); + _expectMapEquals(actual.float64ValuesByName, expected.float64ValuesByName); + _expectMapEquals( + actual.timestampValuesByName, + expected.timestampValuesByName, + ); + _expectMapEquals(actual.durationValuesByName, expected.durationValuesByName); + _expectMapEquals(actual.enumValuesByName, expected.enumValuesByName); } void _expectExampleMessageUnionEquals( diff --git a/integration_tests/idl_tests/go/idl_roundtrip_test.go b/integration_tests/idl_tests/go/idl_roundtrip_test.go index 02de5689fd..ec41e120ca 100644 --- a/integration_tests/idl_tests/go/idl_roundtrip_test.go +++ b/integration_tests/idl_tests/go/idl_roundtrip_test.go @@ -513,6 +513,27 @@ func buildExampleMessage() example.ExampleMessage { Uint8ArrayValuesByName: map[string][]uint8{"u8": {201, 202}}, Float32ArrayValuesByName: map[string][]float32{"f32": {1.25, 2.5}}, Int32ArrayValuesByName: map[string][]int32{"i32": {101, 202}}, + StringValuesByDate: map[fory.Date]string{{Year: 2024, Month: time.May, Day: 7}: "date-key"}, + BoolValuesByName: map[string]bool{"bool": true}, + Int8ValuesByName: map[string]int8{"int8": -8}, + Int16ValuesByName: map[string]int16{"int16": -16}, + FixedI32ValuesByName: map[string]int32{"fixed-i32": -32}, + VarintI32ValuesByName: map[string]int32{"varint-i32": 32}, + FixedI64ValuesByName: map[string]int64{"fixed-i64": -64}, + VarintI64ValuesByName: map[string]int64{"varint-i64": 64}, + TaggedI64ValuesByName: map[string]int64{"tagged-i64": 65}, + Uint8ValuesByName: map[string]uint8{"uint8": 208}, + Uint16ValuesByName: map[string]uint16{"uint16": 60001}, + FixedU32ValuesByName: map[string]uint32{"fixed-u32": 1234567892}, + VarintU32ValuesByName: map[string]uint32{"varint-u32": 1234567893}, + FixedU64ValuesByName: map[string]uint64{"fixed-u64": 9876543213}, + VarintU64ValuesByName: map[string]uint64{"varint-u64": 9876543214}, + TaggedU64ValuesByName: map[string]uint64{"tagged-u64": 9876543215}, + Float32ValuesByName: map[string]float32{"float32": 3.25}, + Float64ValuesByName: map[string]float64{"float64": 6.5}, + TimestampValuesByName: map[string]time.Time{"timestamp": time.Date(2024, 6, 7, 8, 9, 10, 0, time.UTC)}, + DurationValuesByName: map[string]time.Duration{"duration": 10 * time.Second}, + EnumValuesByName: map[string]example.ExampleState{"enum": example.ExampleStateFailed}, } setOptionalExampleMessageFields(&message) return message @@ -628,6 +649,13 @@ func normalizeExampleMessageForCompare(message example.ExampleMessage) example.E } message.StringValuesByTimestamp = normalized } + if message.TimestampValuesByName != nil { + normalized := make(map[string]time.Time, len(message.TimestampValuesByName)) + for key, value := range message.TimestampValuesByName { + normalized[key] = value.UTC() + } + message.TimestampValuesByName = normalized + } value := reflect.ValueOf(&message).Elem() for i := 0; i < value.NumField(); i++ { field := value.Field(i) diff --git a/integration_tests/idl_tests/idl/example.fdl b/integration_tests/idl_tests/idl/example.fdl index 7e29609918..4ade5cd856 100644 --- a/integration_tests/idl_tests/idl/example.fdl +++ b/integration_tests/idl_tests/idl/example.fdl @@ -145,6 +145,27 @@ message ExampleMessage [id=1500, evolving=true] { map> uint8_array_values_by_name = 231; map> float32_array_values_by_name = 232; map> int32_array_values_by_name = 233; + map string_values_by_date = 234; + map bool_values_by_name = 236; + map int8_values_by_name = 237; + map int16_values_by_name = 238; + map fixed_i32_values_by_name = 239; + map varint_i32_values_by_name = 240; + map fixed_i64_values_by_name = 241; + map varint_i64_values_by_name = 242; + map tagged_i64_values_by_name = 243; + map uint8_values_by_name = 244; + map uint16_values_by_name = 245; + map fixed_u32_values_by_name = 246; + map varint_u32_values_by_name = 247; + map fixed_u64_values_by_name = 248; + map varint_u64_values_by_name = 249; + map tagged_u64_values_by_name = 250; + map float32_values_by_name = 251; + map float64_values_by_name = 252; + map timestamp_values_by_name = 253; + map duration_values_by_name = 254; + map enum_values_by_name = 255; } union ExampleMessageUnion [id=1501] { @@ -254,4 +275,25 @@ union ExampleMessageUnion [id=1501] { map> uint8_array_values_by_name = 231; map> float32_array_values_by_name = 232; map> int32_array_values_by_name = 233; + map string_values_by_date = 234; + map bool_values_by_name = 236; + map int8_values_by_name = 237; + map int16_values_by_name = 238; + map fixed_i32_values_by_name = 239; + map varint_i32_values_by_name = 240; + map fixed_i64_values_by_name = 241; + map varint_i64_values_by_name = 242; + map tagged_i64_values_by_name = 243; + map uint8_values_by_name = 244; + map uint16_values_by_name = 245; + map fixed_u32_values_by_name = 246; + map varint_u32_values_by_name = 247; + map fixed_u64_values_by_name = 248; + map varint_u64_values_by_name = 249; + map tagged_u64_values_by_name = 250; + map float32_values_by_name = 251; + map float64_values_by_name = 252; + map timestamp_values_by_name = 253; + map duration_values_by_name = 254; + map enum_values_by_name = 255; } diff --git a/integration_tests/idl_tests/java/src/test/java/org/apache/fory/idl_tests/IdlRoundTripTest.java b/integration_tests/idl_tests/java/src/test/java/org/apache/fory/idl_tests/IdlRoundTripTest.java index ae702146a1..cd1f2ea0f0 100644 --- a/integration_tests/idl_tests/java/src/test/java/org/apache/fory/idl_tests/IdlRoundTripTest.java +++ b/integration_tests/idl_tests/java/src/test/java/org/apache/fory/idl_tests/IdlRoundTripTest.java @@ -27,6 +27,10 @@ import addressbook.Person; import addressbook.Person.PhoneNumber; import addressbook.Person.PhoneType; +import any_example.AnyExampleForyRegistration; +import any_example.AnyHolder; +import any_example.AnyInner; +import any_example.AnyUnion; import auto_id.AutoIdForyRegistration; import auto_id.Envelope; import auto_id.Wrapper; @@ -35,12 +39,6 @@ import collection.NumericCollectionUnion; import collection.NumericCollections; import collection.NumericCollectionsArray; -import complex_pb.ComplexPbForyRegistration; -import complex_pb.PrimitiveTypes; -import any_example.AnyExampleForyRegistration; -import any_example.AnyHolder; -import any_example.AnyInner; -import any_example.AnyUnion; import complex_fbs.ComplexFbsForyRegistration; import complex_fbs.Container; import complex_fbs.Metric; @@ -48,6 +46,8 @@ import complex_fbs.Payload; import complex_fbs.ScalarPack; import complex_fbs.Status; +import complex_pb.ComplexPbForyRegistration; +import complex_pb.PrimitiveTypes; import evolving1.Evolving1ForyRegistration; import evolving1.EvolvingMessage; import evolving1.EvolvingSizeMessage; @@ -64,17 +64,6 @@ import graph.Graph; import graph.GraphForyRegistration; import graph.Node; -import root.MultiHolder; -import optional_types.AllOptionalTypes; -import optional_types.OptionalHolder; -import optional_types.OptionalTypesForyRegistration; -import optional_types.OptionalUnion; -import tree.TreeForyRegistration; -import tree.TreeNode; -import monster.Color; -import monster.Monster; -import monster.MonsterForyRegistration; -import monster.Vec3; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -94,6 +83,14 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import monster.Color; +import monster.Monster; +import monster.MonsterForyRegistration; +import monster.Vec3; +import optional_types.AllOptionalTypes; +import optional_types.OptionalHolder; +import optional_types.OptionalTypesForyRegistration; +import optional_types.OptionalUnion; import org.apache.fory.Fory; import org.apache.fory.collection.BFloat16List; import org.apache.fory.collection.BoolList; @@ -114,6 +111,9 @@ import org.apache.fory.type.Float16Array; import org.testng.Assert; import org.testng.annotations.Test; +import root.MultiHolder; +import tree.TreeForyRegistration; +import tree.TreeNode; public class IdlRoundTripTest { @@ -266,8 +266,7 @@ private void runEvolvingRoundTrip() { Object fixedSizeDecoded = foryV2.deserialize(fixedSizeBytes); Assert.assertTrue(fixedSizeDecoded instanceof evolving2.FixedSizeMessage); Assert.assertEquals( - ((evolving2.FixedSizeMessage) fixedSizeDecoded).getPayload(), - fixedSizeV1.getPayload()); + ((evolving2.FixedSizeMessage) fixedSizeDecoded).getPayload(), fixedSizeV1.getPayload()); Object fixedSizeRoundTrip = foryV1.deserialize(foryV2.serialize(fixedSizeDecoded)); Assert.assertTrue(fixedSizeRoundTrip instanceof FixedSizeMessage); Assert.assertEquals(fixedSizeRoundTrip, fixedSizeV1); @@ -399,18 +398,15 @@ private void runCollectionRoundTrip(boolean compatible) throws Exception { assertNumericCollectionArrayUnion(arrayUnionDecoded, collectionArrayUnion); for (String peer : resolvePeers()) { - Path collectionsFile = - Files.createTempFile("idl-collections-" + peer + "-", ".bin"); + Path collectionsFile = Files.createTempFile("idl-collections-" + peer + "-", ".bin"); collectionsFile.toFile().deleteOnExit(); Files.write(collectionsFile, collectionsBytes); - Path unionFile = - Files.createTempFile("idl-collection-union-" + peer + "-", ".bin"); + Path unionFile = Files.createTempFile("idl-collection-union-" + peer + "-", ".bin"); unionFile.toFile().deleteOnExit(); Files.write(unionFile, unionBytes); - Path arrayFile = - Files.createTempFile("idl-collections-array-" + peer + "-", ".bin"); + Path arrayFile = Files.createTempFile("idl-collections-array-" + peer + "-", ".bin"); arrayFile.toFile().deleteOnExit(); Files.write(arrayFile, arrayBytes); @@ -423,8 +419,7 @@ private void runCollectionRoundTrip(boolean compatible) throws Exception { env.put("DATA_FILE_COLLECTION", collectionsFile.toAbsolutePath().toString()); env.put("DATA_FILE_COLLECTION_UNION", unionFile.toAbsolutePath().toString()); env.put("DATA_FILE_COLLECTION_ARRAY", arrayFile.toAbsolutePath().toString()); - env.put( - "DATA_FILE_COLLECTION_ARRAY_UNION", arrayUnionFile.toAbsolutePath().toString()); + env.put("DATA_FILE_COLLECTION_ARRAY_UNION", arrayUnionFile.toAbsolutePath().toString()); PeerCommand command = buildPeerCommand(peer, env, compatible); runPeer(command, peer); @@ -667,13 +662,11 @@ private void runFlatbuffersRoundTrip(boolean compatible) throws Exception { Assert.assertEquals(containerDecoded, container); for (String peer : resolvePeers()) { - Path monsterFile = - Files.createTempFile("idl-flatbuffers-monster-" + peer + "-", ".bin"); + Path monsterFile = Files.createTempFile("idl-flatbuffers-monster-" + peer + "-", ".bin"); monsterFile.toFile().deleteOnExit(); Files.write(monsterFile, monsterBytes); - Path containerFile = - Files.createTempFile("idl-flatbuffers-test2-" + peer + "-", ".bin"); + Path containerFile = Files.createTempFile("idl-flatbuffers-test2-" + peer + "-", ".bin"); containerFile.toFile().deleteOnExit(); Files.write(containerFile, containerBytes); @@ -696,18 +689,11 @@ private void runFlatbuffersRoundTrip(boolean compatible) throws Exception { } private Fory buildFory(boolean compatible) { - return Fory.builder() - .withXlang(true) - .withCompatible(compatible) - .build(); + return Fory.builder().withXlang(true).withCompatible(compatible).build(); } private Fory buildRefFory(boolean compatible) { - return Fory.builder() - .withXlang(true) - .withCompatible(compatible) - .withRefTracking(true) - .build(); + return Fory.builder().withXlang(true).withCompatible(compatible).withRefTracking(true).build(); } private List resolvePeers() { @@ -802,10 +788,7 @@ private PeerCommand buildPeerCommand( workDir = idlRoot.resolve("dart"); command = Arrays.asList( - "dart", - "test", - "--name", - "interop file roundtrip hooks when env vars are set"); + "dart", "test", "--name", "interop file roundtrip hooks when env vars are set"); peerCommand.environment.put("ENABLE_FORY_DEBUG_OUTPUT", "1"); break; default: @@ -834,9 +817,7 @@ private void runPeer(PeerCommand command, String peer) throws IOException, Inter process.waitFor(10, TimeUnit.SECONDS); String output = outputCollector.awaitOutput(); Assert.fail( - "Peer process timed out for " - + peer - + (output.isEmpty() ? "" : "\noutput:\n" + output)); + "Peer process timed out for " + peer + (output.isEmpty() ? "" : "\noutput:\n" + output)); } int exitCode = process.exitValue(); @@ -1085,8 +1066,7 @@ private ExampleMessage buildExampleMessage() { message.setDateList(Arrays.asList(LocalDate.of(2024, 1, 1), LocalDate.of(2024, 1, 2))); message.setTimestampList( Arrays.asList( - Instant.parse("2024-01-01T00:00:00Z"), - Instant.parse("2024-01-02T00:00:00Z"))); + Instant.parse("2024-01-01T00:00:00Z"), Instant.parse("2024-01-02T00:00:00Z"))); message.setDurationList(Arrays.asList(Duration.ofMillis(1), Duration.ofSeconds(2))); message.setDecimalList(Arrays.asList(new BigDecimal("1.25"), new BigDecimal("2.50"))); message.setEnumList(Arrays.asList(ExampleState.UNKNOWN, ExampleState.FAILED)); @@ -1144,6 +1124,27 @@ private ExampleMessage buildExampleMessage() { message.setUint8ArrayValuesByName(Map.of("u8", new byte[] {(byte) 201, (byte) 202})); message.setFloat32ArrayValuesByName(Map.of("f32", new float[] {1.25f, 2.5f})); message.setInt32ArrayValuesByName(Map.of("i32", new int[] {101, 202})); + message.setStringValuesByDate(Map.of(LocalDate.of(2024, 5, 7), "date-key")); + message.setBoolValuesByName(Map.of("bool", true)); + message.setInt8ValuesByName(Map.of("int8", (byte) -8)); + message.setInt16ValuesByName(Map.of("int16", (short) -16)); + message.setFixedI32ValuesByName(Map.of("fixed-i32", -32)); + message.setVarintI32ValuesByName(Map.of("varint-i32", 32)); + message.setFixedI64ValuesByName(Map.of("fixed-i64", -64L)); + message.setVarintI64ValuesByName(Map.of("varint-i64", 64L)); + message.setTaggedI64ValuesByName(Map.of("tagged-i64", 65L)); + message.setUint8ValuesByName(Map.of("uint8", 208)); + message.setUint16ValuesByName(Map.of("uint16", 60001)); + message.setFixedU32ValuesByName(Map.of("fixed-u32", 1234567892L)); + message.setVarintU32ValuesByName(Map.of("varint-u32", 1234567893L)); + message.setFixedU64ValuesByName(Map.of("fixed-u64", 9876543213L)); + message.setVarintU64ValuesByName(Map.of("varint-u64", 9876543214L)); + message.setTaggedU64ValuesByName(Map.of("tagged-u64", 9876543215L)); + message.setFloat32ValuesByName(Map.of("float32", 3.25f)); + message.setFloat64ValuesByName(Map.of("float64", 6.5d)); + message.setTimestampValuesByName(Map.of("timestamp", Instant.parse("2024-06-07T08:09:10Z"))); + message.setDurationValuesByName(Map.of("duration", Duration.ofSeconds(10))); + message.setEnumValuesByName(Map.of("enum", ExampleState.FAILED)); return message; } @@ -1159,11 +1160,11 @@ private ExampleMessageUnion buildExampleUnion() { Arrays.asList(new int[] {11, 12}, new int[] {13, 14})); } - private void assertNumericCollectionUnion( - Object decoded, NumericCollectionUnion expected) { + private void assertNumericCollectionUnion(Object decoded, NumericCollectionUnion expected) { Assert.assertTrue(decoded instanceof NumericCollectionUnion); NumericCollectionUnion union = (NumericCollectionUnion) decoded; - Assert.assertEquals(union.getNumericCollectionUnionCase(), expected.getNumericCollectionUnionCase()); + Assert.assertEquals( + union.getNumericCollectionUnionCase(), expected.getNumericCollectionUnionCase()); switch (union.getNumericCollectionUnionCase()) { case INT32_VALUES: Assert.assertEquals(union.getInt32Values(), expected.getInt32Values()); @@ -1178,16 +1179,13 @@ private void assertNumericCollectionArrayUnion( Assert.assertTrue(decoded instanceof NumericCollectionArrayUnion); NumericCollectionArrayUnion union = (NumericCollectionArrayUnion) decoded; Assert.assertEquals( - union.getNumericCollectionArrayUnionCase(), - expected.getNumericCollectionArrayUnionCase()); + union.getNumericCollectionArrayUnionCase(), expected.getNumericCollectionArrayUnionCase()); switch (union.getNumericCollectionArrayUnionCase()) { case UINT16_VALUES: - Assert.assertTrue( - Arrays.equals(union.getUint16Values(), expected.getUint16Values())); + Assert.assertTrue(Arrays.equals(union.getUint16Values(), expected.getUint16Values())); break; default: - Assert.fail( - "Unexpected array union case: " + union.getNumericCollectionArrayUnionCase()); + Assert.fail("Unexpected array union case: " + union.getNumericCollectionArrayUnionCase()); } } diff --git a/integration_tests/idl_tests/javascript/roundtrip.ts b/integration_tests/idl_tests/javascript/roundtrip.ts index 020747427f..23af999e0b 100644 --- a/integration_tests/idl_tests/javascript/roundtrip.ts +++ b/integration_tests/idl_tests/javascript/roundtrip.ts @@ -147,11 +147,10 @@ function resolveRootSerializer(fory: Fory, bytes: Uint8Array): Serializer { // serializer detection from the payload, then map back to the local // registered serializer when available. const detectedSerializer = AnyHelper.detectSerializer(fory.readContext); - const resolvedSerializer = ( + const resolvedSerializer = fory.typeResolver.getSerializerByTypeInfo( detectedSerializer.getTypeInfo(), - ) ?? detectedSerializer - ); + ) ?? detectedSerializer; return resolvedSerializer; } @@ -514,7 +513,9 @@ function buildExampleMessage(): ExampleMessage { stringValuesByVarintU64: new Map([[9876543211n, "varint-u64"]]), stringValuesByTaggedU64: new Map([[9876543212n, "tagged-u64"]]), stringValuesByString: new Map([["name", "value"]]), - stringValuesByTimestamp: new Map([[new Date("2024-03-04T05:06:07Z"), "time"]]), + stringValuesByTimestamp: new Map([ + [new Date("2024-03-04T05:06:07Z"), "time"], + ]), stringValuesByDuration: new Map([[9000, "duration"]]), stringValuesByEnum: new Map([[ExampleState.READY, "ready"]]), float16ValuesByName: new Map([["f16", 1.25]]), @@ -529,10 +530,31 @@ function buildExampleMessage(): ExampleMessage { ["union", { case: ExampleLeafUnionCase.CODE, value: 42 }], ]), uint8ArrayValuesByName: new Map([["u8", new Uint8Array([201, 202])]]), - float32ArrayValuesByName: new Map([ - ["f32", new Float32Array([1.25, 2.5])], - ]), + float32ArrayValuesByName: new Map([["f32", new Float32Array([1.25, 2.5])]]), int32ArrayValuesByName: new Map([["i32", new Int32Array([101, 202])]]), + stringValuesByDate: new Map([[buildLocalDate(2024, 5, 7), "date-key"]]), + boolValuesByName: new Map([["bool", true]]), + int8ValuesByName: new Map([["int8", -8]]), + int16ValuesByName: new Map([["int16", -16]]), + fixedI32ValuesByName: new Map([["fixed-i32", -32]]), + varintI32ValuesByName: new Map([["varint-i32", 32]]), + fixedI64ValuesByName: new Map([["fixed-i64", -64n]]), + varintI64ValuesByName: new Map([["varint-i64", 64n]]), + taggedI64ValuesByName: new Map([["tagged-i64", 65n]]), + uint8ValuesByName: new Map([["uint8", 208]]), + uint16ValuesByName: new Map([["uint16", 60001]]), + fixedU32ValuesByName: new Map([["fixed-u32", 1234567892]]), + varintU32ValuesByName: new Map([["varint-u32", 1234567893]]), + fixedU64ValuesByName: new Map([["fixed-u64", 9876543213n]]), + varintU64ValuesByName: new Map([["varint-u64", 9876543214n]]), + taggedU64ValuesByName: new Map([["tagged-u64", 9876543215n]]), + float32ValuesByName: new Map([["float32", 3.25]]), + float64ValuesByName: new Map([["float64", 6.5]]), + timestampValuesByName: new Map([ + ["timestamp", new Date("2024-06-07T08:09:10Z")], + ]), + durationValuesByName: new Map([["duration", 10000]]), + enumValuesByName: new Map([["enum", ExampleState.FAILED]]), }; } diff --git a/integration_tests/idl_tests/javascript/test/roundtrip.test.ts b/integration_tests/idl_tests/javascript/test/roundtrip.test.ts index 953b79af65..2d3a3b2764 100644 --- a/integration_tests/idl_tests/javascript/test/roundtrip.test.ts +++ b/integration_tests/idl_tests/javascript/test/roundtrip.test.ts @@ -460,7 +460,9 @@ function buildExampleMessage(): ExampleMessage { stringValuesByVarintU64: new Map([[9876543211n, "varint-u64"]]), stringValuesByTaggedU64: new Map([[9876543212n, "tagged-u64"]]), stringValuesByString: new Map([["name", "value"]]), - stringValuesByTimestamp: new Map([[new Date("2024-03-04T05:06:07Z"), "time"]]), + stringValuesByTimestamp: new Map([ + [new Date("2024-03-04T05:06:07Z"), "time"], + ]), stringValuesByDuration: new Map([[9000, "duration"]]), stringValuesByEnum: new Map([[ExampleState.READY, "ready"]]), float16ValuesByName: new Map([["f16", 1.25]]), @@ -475,10 +477,31 @@ function buildExampleMessage(): ExampleMessage { ["union", { case: ExampleLeafUnionCase.CODE, value: 42 }], ]), uint8ArrayValuesByName: new Map([["u8", new Uint8Array([201, 202])]]), - float32ArrayValuesByName: new Map([ - ["f32", new Float32Array([1.25, 2.5])], - ]), + float32ArrayValuesByName: new Map([["f32", new Float32Array([1.25, 2.5])]]), int32ArrayValuesByName: new Map([["i32", new Int32Array([101, 202])]]), + stringValuesByDate: new Map([[buildLocalDate(2024, 5, 7), "date-key"]]), + boolValuesByName: new Map([["bool", true]]), + int8ValuesByName: new Map([["int8", -8]]), + int16ValuesByName: new Map([["int16", -16]]), + fixedI32ValuesByName: new Map([["fixed-i32", -32]]), + varintI32ValuesByName: new Map([["varint-i32", 32]]), + fixedI64ValuesByName: new Map([["fixed-i64", -64n]]), + varintI64ValuesByName: new Map([["varint-i64", 64n]]), + taggedI64ValuesByName: new Map([["tagged-i64", 65n]]), + uint8ValuesByName: new Map([["uint8", 208]]), + uint16ValuesByName: new Map([["uint16", 60001]]), + fixedU32ValuesByName: new Map([["fixed-u32", 1234567892]]), + varintU32ValuesByName: new Map([["varint-u32", 1234567893]]), + fixedU64ValuesByName: new Map([["fixed-u64", 9876543213n]]), + varintU64ValuesByName: new Map([["varint-u64", 9876543214n]]), + taggedU64ValuesByName: new Map([["tagged-u64", 9876543215n]]), + float32ValuesByName: new Map([["float32", 3.25]]), + float64ValuesByName: new Map([["float64", 6.5]]), + timestampValuesByName: new Map([ + ["timestamp", new Date("2024-06-07T08:09:10Z")], + ]), + durationValuesByName: new Map([["duration", 10000]]), + enumValuesByName: new Map([["enum", ExampleState.FAILED]]), }; } diff --git a/integration_tests/idl_tests/python/idl_tests/roundtrip.py b/integration_tests/idl_tests/python/idl_tests/roundtrip.py index 76bdb37316..942386c057 100644 --- a/integration_tests/idl_tests/python/idl_tests/roundtrip.py +++ b/integration_tests/idl_tests/python/idl_tests/roundtrip.py @@ -540,6 +540,31 @@ def build_example_message() -> "example.ExampleMessage": union_values_by_name={"union": example.ExampleLeafUnion.code(42)}, uint8_array_values_by_name={"u8": np.array([201, 202], dtype=np.uint8)}, float32_array_values_by_name={"f32": np.array([1.25, 2.5], dtype=np.float32)}, + string_values_by_date={datetime.date(2024, 5, 7): "date-key"}, + bool_values_by_name={"bool": True}, + int8_values_by_name={"int8": -8}, + int16_values_by_name={"int16": -16}, + fixed_i32_values_by_name={"fixed-i32": -32}, + varint_i32_values_by_name={"varint-i32": 32}, + fixed_i64_values_by_name={"fixed-i64": -64}, + varint_i64_values_by_name={"varint-i64": 64}, + tagged_i64_values_by_name={"tagged-i64": 65}, + uint8_values_by_name={"uint8": 208}, + uint16_values_by_name={"uint16": 60001}, + fixed_u32_values_by_name={"fixed-u32": 1234567892}, + varint_u32_values_by_name={"varint-u32": 1234567893}, + fixed_u64_values_by_name={"fixed-u64": 9876543213}, + varint_u64_values_by_name={"varint-u64": 9876543214}, + tagged_u64_values_by_name={"tagged-u64": 9876543215}, + float32_values_by_name={"float32": 3.25}, + float64_values_by_name={"float64": 6.5}, + timestamp_values_by_name={ + "timestamp": datetime.datetime( + 2024, 6, 7, 8, 9, 10, tzinfo=datetime.timezone.utc + ) + }, + duration_values_by_name={"duration": datetime.timedelta(seconds=10)}, + enum_values_by_name={"enum": example.ExampleState.FAILED}, ) fields = example.ExampleMessage.__dataclass_fields__ if "uint8_array_list" in fields: diff --git a/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs b/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs index 201c5359f7..1e1b7b1c21 100644 --- a/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs +++ b/integration_tests/idl_tests/rust/tests/idl_roundtrip.rs @@ -32,7 +32,7 @@ use idl_tests::generated::collection::{ self, NumericCollectionArrayUnion, NumericCollectionUnion, NumericCollections, NumericCollectionsArray, }; -use idl_tests::generated::complex_fbs::{self, Container, Note, Payload, ScalarPack, Status}; +use idl_tests::generated::complex_fbs::{self, Container, Payload, ScalarPack, Status}; use idl_tests::generated::complex_pb::{self, PrimitiveTypes}; use idl_tests::generated::evolving1; use idl_tests::generated::evolving2; @@ -54,11 +54,7 @@ fn build_address_book() -> AddressBook { phone_type: PhoneType::Work, }; - let mut pet = Animal::Dog(Dog { - name: "Rex".to_string(), - bark_volume: 5, - }); - pet = Animal::Cat(Cat { + let pet = Animal::Cat(Cat { name: "Mimi".to_string(), lives: 9, }); @@ -152,8 +148,7 @@ fn test_to_bytes_from_bytes() { } fn build_primitive_types() -> PrimitiveTypes { - let mut contact = complex_pb::primitive_types::Contact::Email("alice@example.com".to_string()); - contact = complex_pb::primitive_types::Contact::Phone(12345); + let contact = complex_pb::primitive_types::Contact::Phone(12345); PrimitiveTypes { bool_value: true, @@ -246,10 +241,7 @@ fn build_container() -> Container { d: 2.5, ok: true, }; - let mut payload = Payload::Note(Note { - text: "alpha".to_string(), - }); - payload = Payload::Metric(complex_fbs::Metric { value: 42.0 }); + let payload = Payload::Metric(complex_fbs::Metric { value: 42.0 }); Container { id: 9876543210, @@ -519,6 +511,39 @@ fn build_example_message() -> ExampleMessage { uint8_array_values_by_name: HashMap::from([("u8".to_string(), vec![201, 202])]), float32_array_values_by_name: HashMap::from([("f32".to_string(), vec![1.25, 2.5])]), int32_array_values_by_name: HashMap::from([("i32".to_string(), vec![101, 202])]), + string_values_by_date: HashMap::from([( + NaiveDate::from_ymd_opt(2024, 5, 7).unwrap(), + "date-key".to_string(), + )]), + bool_values_by_name: HashMap::from([("bool".to_string(), true)]), + int8_values_by_name: HashMap::from([("int8".to_string(), -8)]), + int16_values_by_name: HashMap::from([("int16".to_string(), -16)]), + fixed_i32_values_by_name: HashMap::from([("fixed-i32".to_string(), -32)]), + varint_i32_values_by_name: HashMap::from([("varint-i32".to_string(), 32)]), + fixed_i64_values_by_name: HashMap::from([("fixed-i64".to_string(), -64)]), + varint_i64_values_by_name: HashMap::from([("varint-i64".to_string(), 64)]), + tagged_i64_values_by_name: HashMap::from([("tagged-i64".to_string(), 65)]), + uint8_values_by_name: HashMap::from([("uint8".to_string(), 208)]), + uint16_values_by_name: HashMap::from([("uint16".to_string(), 60001)]), + fixed_u32_values_by_name: HashMap::from([("fixed-u32".to_string(), 1234567892)]), + varint_u32_values_by_name: HashMap::from([("varint-u32".to_string(), 1234567893)]), + fixed_u64_values_by_name: HashMap::from([("fixed-u64".to_string(), 9876543213)]), + varint_u64_values_by_name: HashMap::from([("varint-u64".to_string(), 9876543214)]), + tagged_u64_values_by_name: HashMap::from([("tagged-u64".to_string(), 9876543215)]), + float32_values_by_name: HashMap::from([("float32".to_string(), 3.25)]), + float64_values_by_name: HashMap::from([("float64".to_string(), 6.5)]), + timestamp_values_by_name: HashMap::from([( + "timestamp".to_string(), + NaiveDate::from_ymd_opt(2024, 6, 7) + .unwrap() + .and_hms_opt(8, 9, 10) + .unwrap(), + )]), + duration_values_by_name: HashMap::from([( + "duration".to_string(), + chrono::Duration::seconds(10), + )]), + enum_values_by_name: HashMap::from([("enum".to_string(), ExampleState::Failed)]), } } diff --git a/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift b/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift index 4c1a738409..7e80b7aad4 100644 --- a/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift +++ b/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift @@ -562,7 +562,28 @@ final class IdlRoundTripTests: XCTestCase { unionValuesByName: ["union": .code(42)], uint8ArrayValuesByName: ["u8": [201, 202]], float32ArrayValuesByName: ["f32": [1.25, 2.5]], - int32ArrayValuesByName: ["i32": [101, 202]] + int32ArrayValuesByName: ["i32": [101, 202]], + stringValuesByDate: [try! LocalDate(year: 2024, month: 5, day: 7): "date-key"], + boolValuesByName: ["bool": true], + int8ValuesByName: ["int8": -8], + int16ValuesByName: ["int16": -16], + fixedI32ValuesByName: ["fixed-i32": -32], + varintI32ValuesByName: ["varint-i32": 32], + fixedI64ValuesByName: ["fixed-i64": -64], + varintI64ValuesByName: ["varint-i64": 64], + taggedI64ValuesByName: ["tagged-i64": 65], + uint8ValuesByName: ["uint8": 208], + uint16ValuesByName: ["uint16": 60001], + fixedU32ValuesByName: ["fixed-u32": 1234567892], + varintU32ValuesByName: ["varint-u32": 1234567893], + fixedU64ValuesByName: ["fixed-u64": 9876543213], + varintU64ValuesByName: ["varint-u64": 9876543214], + taggedU64ValuesByName: ["tagged-u64": 9876543215], + float32ValuesByName: ["float32": 3.25], + float64ValuesByName: ["float64": 6.5], + timestampValuesByName: ["timestamp": Date(timeIntervalSince1970: 1717747750)], + durationValuesByName: ["duration": .seconds(10)], + enumValuesByName: ["enum": .failed] ) } @@ -618,6 +639,27 @@ final class IdlRoundTripTests: XCTestCase { XCTAssertEqual(actual.uint8ArrayValuesByName, expected.uint8ArrayValuesByName) XCTAssertEqual(actual.float32ArrayValuesByName, expected.float32ArrayValuesByName) XCTAssertEqual(actual.int32ArrayValuesByName, expected.int32ArrayValuesByName) + XCTAssertEqual(actual.stringValuesByDate, expected.stringValuesByDate) + XCTAssertEqual(actual.boolValuesByName, expected.boolValuesByName) + XCTAssertEqual(actual.int8ValuesByName, expected.int8ValuesByName) + XCTAssertEqual(actual.int16ValuesByName, expected.int16ValuesByName) + XCTAssertEqual(actual.fixedI32ValuesByName, expected.fixedI32ValuesByName) + XCTAssertEqual(actual.varintI32ValuesByName, expected.varintI32ValuesByName) + XCTAssertEqual(actual.fixedI64ValuesByName, expected.fixedI64ValuesByName) + XCTAssertEqual(actual.varintI64ValuesByName, expected.varintI64ValuesByName) + XCTAssertEqual(actual.taggedI64ValuesByName, expected.taggedI64ValuesByName) + XCTAssertEqual(actual.uint8ValuesByName, expected.uint8ValuesByName) + XCTAssertEqual(actual.uint16ValuesByName, expected.uint16ValuesByName) + XCTAssertEqual(actual.fixedU32ValuesByName, expected.fixedU32ValuesByName) + XCTAssertEqual(actual.varintU32ValuesByName, expected.varintU32ValuesByName) + XCTAssertEqual(actual.fixedU64ValuesByName, expected.fixedU64ValuesByName) + XCTAssertEqual(actual.varintU64ValuesByName, expected.varintU64ValuesByName) + XCTAssertEqual(actual.taggedU64ValuesByName, expected.taggedU64ValuesByName) + XCTAssertEqual(actual.float32ValuesByName, expected.float32ValuesByName) + XCTAssertEqual(actual.float64ValuesByName, expected.float64ValuesByName) + XCTAssertEqual(actual.timestampValuesByName, expected.timestampValuesByName) + XCTAssertEqual(actual.durationValuesByName, expected.durationValuesByName) + XCTAssertEqual(actual.enumValuesByName, expected.enumValuesByName) } private func buildOptionalHolder() -> OptionalTypes.OptionalHolder { diff --git a/kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/TypeUseAnnotationTest.kt b/kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/TypeUseAnnotationTest.kt new file mode 100644 index 0000000000..f6c68565bd --- /dev/null +++ b/kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/TypeUseAnnotationTest.kt @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package org.apache.fory.serializer.kotlin + +import org.apache.fory.annotation.Int32Type +import org.apache.fory.annotation.UInt32Type +import org.apache.fory.annotation.UInt64Type +import org.apache.fory.config.Int32Encoding +import org.testng.Assert +import org.testng.annotations.Test + +class TypeUseAnnotationTest { + private class TypeUseCarrier( + val fixedId: @Int32Type(encoding = Int32Encoding.FIXED) Int = 1, + val unsignedId: @UInt32Type Int = 2, + val unsignedValues: List<@UInt64Type Long> = listOf(3L), + val unsignedArrayValues: Array<@UInt32Type Int> = arrayOf(4), + ) + + @Test + fun testJavaScalarAnnotationsCompileAtKotlinTypeUseSites() { + val carrier = TypeUseCarrier() + Assert.assertEquals(carrier.fixedId, 1) + Assert.assertEquals(carrier.unsignedId, 2) + Assert.assertEquals(carrier.unsignedValues, listOf(3L)) + Assert.assertEquals(carrier.unsignedArrayValues.toList(), listOf(4)) + } +} diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs index ed66e6f860..c96e46baf9 100644 --- a/rust/fory/src/lib.rs +++ b/rust/fory/src/lib.rs @@ -806,7 +806,7 @@ //! //! - External types from other crates that you can't modify //! - Types with special serialization requirements -//! - Legacy data format compatibility +//! - Existing external binary format interoperability //! - Performance-critical custom encoding //! - Complex types that require special handling //! diff --git a/scala/src/test/scala/org/apache/fory/serializer/scala/TypeUseAnnotationTest.scala b/scala/src/test/scala/org/apache/fory/serializer/scala/TypeUseAnnotationTest.scala new file mode 100644 index 0000000000..4032eaee19 --- /dev/null +++ b/scala/src/test/scala/org/apache/fory/serializer/scala/TypeUseAnnotationTest.scala @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package org.apache.fory.serializer.scala + +import java.util.Arrays +import org.apache.fory.annotation.{Int32Type, UInt32Type, UInt64Type} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +final class ScalaTypeUseCarrier { + val fixedId: Int @Int32Type = 1 + val unsignedId: Int @UInt32Type = 2 + val unsignedValues: java.util.List[Long @UInt64Type] = Arrays.asList(3L) +} + +class TypeUseAnnotationTest extends AnyWordSpec with Matchers { + "Java scalar annotations" should { + "compile at Scala type positions" in { + val carrier = new ScalaTypeUseCarrier + carrier.fixedId shouldBe 1 + carrier.unsignedId shouldBe 2 + carrier.unsignedValues shouldEqual Arrays.asList(3L) + } + } +}