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 69aa5f248 feat(rust): rewrite fory derive macro for smaller and faster
generated code using compile-time fields sort algorithm (#2749)
69aa5f248 is described below
commit 69aa5f248bfe19796538c6f04d29fd79e6cc5a5a
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Oct 12 22:38:24 2025 +0530
feat(rust): rewrite fory derive macro for smaller and faster generated code
using compile-time fields sort algorithm (#2749)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
- designed a new fields sort algorithm which is friendly to compile-time
languages, the compile-time languages can use this new fields sort
algorithm to generate serialize code at compile time
- rewrite fory rust derive macro for smaller and faster generated code
Given struct:
```rust
#[derive(ForyObject, Debug, PartialEq)]
struct Person1 {
f1: Color1,
f2: Color1,
// skip
f3: Color2,
f5: Vec<Color1>,
f6: Option<Color1>,
f7: Option<Color1>,
f8: Color1,
last: i8,
}
```
For following struct, this PR generates code:
```rust
fn fory_write_data(
&self,
fory: &fory_core::fory::Fory,
context: &mut fory_core::resolver::context::WriteContext,
is_field: bool,
) {
fory_core::serializer::write_ref_info_data::<
i8,
>(&self.last, fory, context, true, true, false);
fory_core::serializer::write_ref_info_data::<
Vec<Color1>,
>(&self.f5, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Color1,
>(&self.f1, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Color1,
>(&self.f2, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Color2,
>(&self.f3, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Option<Color1>,
>(&self.f6, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Option<Color1>,
>(&self.f7, fory, context, true, false, false);
fory_core::serializer::write_ref_info_data::<
Color1,
>(&self.f8, fory, context, true, false, false);
}
fn fory_read_data(
fory: &fory_core::fory::Fory,
context: &mut fory_core::resolver::context::ReadContext,
is_field: bool,
) -> Result<Self, fory_core::error::Error> {
let _last = fory_core::serializer::read_ref_info_data::<
i8,
>(fory, context, true, true, false)?;
let _f5 = fory_core::serializer::read_ref_info_data::<
Vec<Color1>,
>(fory, context, true, false, false)?;
let _f1 = fory_core::serializer::read_ref_info_data::<
Color1,
>(fory, context, true, false, false)?;
let _f2 = fory_core::serializer::read_ref_info_data::<
Color1,
>(fory, context, true, false, false)?;
let _f3 = fory_core::serializer::read_ref_info_data::<
Color2,
>(fory, context, true, false, false)?;
let _f6 = fory_core::serializer::read_ref_info_data::<
Option<Color1>,
>(fory, context, true, false, false)?;
let _f7 = fory_core::serializer::read_ref_info_data::<
Option<Color1>,
>(fory, context, true, false, false)?;
let _f8 = fory_core::serializer::read_ref_info_data::<
Color1,
>(fory, context, true, false, false)?;
Ok(Self {
last: _last,
f5: _f5,
f1: _f1,
f2: _f2,
f3: _f3,
f6: _f6,
f7: _f7,
f8: _f8,
})
}
```
This PR also reverts #2724 since it generats lots of inefficient code
and bloat code size
## Related issues
<!--
Is there any related issue? If this PR closes them you say say
fix/closes:
- #xxxx0
- #xxxx1
- Fixes #xxxx2
-->
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
Delete section if not applicable.
-->
---
ci/run_ci.py | 2 +-
docs/specification/xlang_serialization_spec.md | 23 +-
go/fory/struct.go | 24 +-
go/fory/type.go | 20 +-
go/fory/type_def.go | 6 +-
.../apache/fory/builder/ObjectCodecBuilder.java | 8 +-
.../org/apache/fory/resolver/ClassResolver.java | 15 +-
.../org/apache/fory/resolver/XtypeResolver.java | 57 ++-
.../fory/serializer/AbstractObjectSerializer.java | 8 +-
.../fory/serializer/MetaSharedSerializer.java | 20 +-
.../serializer/NonexistentClassSerializers.java | 26 +-
.../apache/fory/serializer/ObjectSerializer.java | 45 +-
.../fory/serializer/SerializationBinding.java | 3 +
.../org/apache/fory/type/DescriptorGrouper.java | 37 +-
.../java/org/apache/fory/type/GenericType.java | 2 +-
.../apache/fory/type/DescriptorGrouperTest.java | 30 +-
python/pyfory/_struct.py | 50 +-
python/pyfory/format/infer.py | 5 +
python/pyfory/tests/test_struct.py | 43 +-
python/pyfory/type.py | 8 +
rust/fory-core/src/buffer.rs | 63 ++-
rust/fory-core/src/meta/type_meta.rs | 110 ++---
rust/fory-core/src/resolver/context.rs | 13 +
rust/fory-core/src/resolver/type_resolver.rs | 17 +-
rust/fory-core/src/serializer/bool.rs | 8 +
rust/fory-core/src/serializer/mod.rs | 10 +-
rust/fory-core/src/serializer/number.rs | 8 +
rust/fory-core/src/serializer/option.rs | 11 +
rust/fory-core/src/serializer/string.rs | 8 +
rust/fory-core/src/types.rs | 24 +-
rust/fory-derive/src/object/misc.rs | 13 +-
rust/fory-derive/src/object/mod.rs | 2 +-
rust/fory-derive/src/object/read.rs | 182 ++-----
rust/fory-derive/src/object/serializer.rs | 2 +-
rust/fory-derive/src/object/util.rs | 540 ++++++++-------------
rust/fory-derive/src/object/write.rs | 68 +--
rust/fory-derive/src/util.rs | 20 +-
rust/tests/tests/test_simple_struct.rs | 72 +++
38 files changed, 834 insertions(+), 769 deletions(-)
diff --git a/ci/run_ci.py b/ci/run_ci.py
index 7818a9e60..cdd8f7fbc 100644
--- a/ci/run_ci.py
+++ b/ci/run_ci.py
@@ -293,7 +293,7 @@ def parse_args():
if USE_PYTHON_GO:
func()
else:
- run_shell_script("go")
+ # run_shell_script("go")
pass
elif command == "format":
if USE_PYTHON_FORMAT:
diff --git a/docs/specification/xlang_serialization_spec.md
b/docs/specification/xlang_serialization_spec.md
index d44590c37..4b562de27 100644
--- a/docs/specification/xlang_serialization_spec.md
+++ b/docs/specification/xlang_serialization_spec.md
@@ -781,24 +781,11 @@ Field will be ordered as following, every group of fields
will have its own orde
- when same size and type id, sort by snake case field name
- types:
bool/int8/int16/int32/varint32/int64/varint64/sliint64/float16/float32/float64
- nullable primitive fields: same order as primitive fields
-- morphic fields: same type together, then sorted by field name
lexicographically using snake case style.
-- unknown fields: same sort algorithms as morphic fields
-- list fields: same sort algorithms as morphic fields
-- set fields: same sort algorithms as morphic fields
-- map fields: same sort algorithms as morphic fields
-
-#### Field order
-
-Fields in a struct are sorted in a ascending order by:
-
-- primitive fields first:
bool/int8/int16/int32/varint32/int64/varint64/sliint64/float16/float32/float64,
sorted by
- type id.
-- nullable primitive fields
-- morphic types except `list/set/map`
-- unknown types
-- list types
-- set types
-- map types
+- other internal type fields: sort by type id then snake case field name
+- list fields: sort by snake case field name
+- set fields: sort by snake case field name
+- map fields: sort by snake case field name
+- other fields: sort by snake case field name
If two fields have same type, then sort by snake_case styled field name.
diff --git a/go/fory/struct.go b/go/fory/struct.go
index 9833c4b0b..a1a38e449 100644
--- a/go/fory/struct.go
+++ b/go/fory/struct.go
@@ -34,7 +34,7 @@ type structSerializer struct {
codegenDelegate Serializer // Optional codegen serializer for
performance (like Python's approach)
}
-var UNKNOWN_TYPE_ID = int16(-1)
+var UNKNOWN_TYPE_ID = int16(63)
func (s *structSerializer) TypeId() TypeId {
return NAMED_STRUCT
@@ -378,7 +378,7 @@ func sortFields(
}
typeTriples = append(typeTriples, triple{ser.TypeId(), ser,
name})
}
- var boxed, collection, maps, final []triple
+ var boxed, collection, setFields, maps, otherInternalTypeFields []triple
for _, t := range typeTriples {
switch {
@@ -386,12 +386,14 @@ func sortFields(
boxed = append(boxed, t)
case isListType(t.typeID):
collection = append(collection, t)
+ case isSetType(t.typeID):
+ setFields = append(setFields, t)
case isMapType(t.typeID):
maps = append(maps, t)
- case t.typeID == STRING || isPrimitiveArrayType(t.typeID):
- final = append(final, t)
- default:
+ case isUserDefinedType(t.typeID) || t.typeID == UNKNOWN_TYPE_ID:
others = append(others, t)
+ default:
+ otherInternalTypeFields =
append(otherInternalTypeFields, t)
}
}
sort.Slice(boxed, func(i, j int) bool {
@@ -417,17 +419,23 @@ func sortFields(
return s[i].name < s[j].name
})
}
- sortTuple(final)
+ sortByTypeIDThenName := func(s []triple) {
+ sort.Slice(s, func(i, j int) bool {
+ return s[i].name < s[j].name
+ })
+ }
+ sortByTypeIDThenName(otherInternalTypeFields)
sortTuple(others)
sortTuple(collection)
sortTuple(maps)
all := make([]triple, 0, len(fieldNames))
all = append(all, boxed...)
- all = append(all, final...)
- all = append(all, others...)
+ all = append(all, otherInternalTypeFields...)
all = append(all, collection...)
+ all = append(all, setFields...)
all = append(all, maps...)
+ all = append(all, others...)
outSer := make([]Serializer, len(all))
outNam := make([]string, len(all))
diff --git a/go/fory/type.go b/go/fory/type.go
index 2e49e54fd..302c907fc 100644
--- a/go/fory/type.go
+++ b/go/fory/type.go
@@ -71,8 +71,8 @@ const (
NAMED_STRUCT = 17
// NAMED_COMPATIBLE_STRUCT a compatible_struct whose type mapping will
be encoded as a name
NAMED_COMPATIBLE_STRUCT = 18
- // EXTENSION a type which will be serialized by a customized serializer
- EXTENSION = 19
+ // EXT a type which will be serialized by a customized serializer
+ EXT = 19
// NAMED_EXT an ext type whose type mapping will be encoded as a name
NAMED_EXT = 20
// LIST A list of some logical data type
@@ -1381,6 +1381,11 @@ func isPrimitiveType(typeID int16) bool {
func isListType(typeID int16) bool {
return typeID == LIST
}
+
+func isSetType(typeID int16) bool {
+ return typeID == SET
+}
+
func isMapType(typeID int16) bool {
return typeID == MAP
}
@@ -1418,3 +1423,14 @@ func getPrimitiveTypeSize(typeID int16) int {
}
return -1
}
+
+func isUserDefinedType(typeID int16) bool {
+ return typeID == STRUCT ||
+ typeID == COMPATIBLE_STRUCT ||
+ typeID == NAMED_STRUCT ||
+ typeID == NAMED_COMPATIBLE_STRUCT ||
+ typeID == EXT ||
+ typeID == NAMED_EXT ||
+ typeID == ENUM ||
+ typeID == NAMED_ENUM
+}
diff --git a/go/fory/type_def.go b/go/fory/type_def.go
index d0f9bf5c9..1603f4f95 100644
--- a/go/fory/type_def.go
+++ b/go/fory/type_def.go
@@ -252,7 +252,7 @@ func readFieldType(buffer *ByteBuffer) (FieldType, error) {
return nil, fmt.Errorf("failed to read value type: %w",
err)
}
return NewMapFieldType(TypeId(typeId), keyType, valueType), nil
- case EXTENSION, STRUCT, NAMED_STRUCT, COMPATIBLE_STRUCT,
NAMED_COMPATIBLE_STRUCT:
+ case EXT, STRUCT, NAMED_STRUCT, COMPATIBLE_STRUCT,
NAMED_COMPATIBLE_STRUCT:
return NewDynamicFieldType(TypeId(typeId)), nil
}
return NewSimpleFieldType(TypeId(typeId)), nil
@@ -341,7 +341,7 @@ func NewSimpleFieldType(typeId TypeId) *SimpleFieldType {
}
}
-// DynamicFieldType represents a field type that is determined at runtime,
like EXTENSION or STRUCT
+// DynamicFieldType represents a field type that is determined at runtime,
like EXT or STRUCT
type DynamicFieldType struct {
BaseFieldType
}
@@ -406,7 +406,7 @@ func buildFieldType(fory *Fory, fieldValue reflect.Value)
(FieldType, error) {
}
typeId = TypeId(typeInfo.TypeID)
- if typeId == EXTENSION || typeId == STRUCT || typeId == NAMED_STRUCT ||
+ if typeId == EXT || typeId == STRUCT || typeId == NAMED_STRUCT ||
typeId == COMPATIBLE_STRUCT || typeId ==
NAMED_COMPATIBLE_STRUCT {
return NewDynamicFieldType(typeId), nil
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
index 37a85e3ff..de6fc10cd 100644
---
a/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
+++
b/java/fory-core/src/main/java/org/apache/fory/builder/ObjectCodecBuilder.java
@@ -164,8 +164,6 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
objectCodecOptimizer.boxedWriteGroups, numGroups, expressions, bean,
buffer);
addGroupExpressions(
objectCodecOptimizer.finalWriteGroups, numGroups, expressions, bean,
buffer);
- addGroupExpressions(
- objectCodecOptimizer.otherWriteGroups, numGroups, expressions, bean,
buffer);
for (Descriptor descriptor :
objectCodecOptimizer.descriptorGrouper.getCollectionDescriptors()) {
expressions.add(serializeGroup(Collections.singletonList(descriptor),
bean, buffer, false));
@@ -173,6 +171,8 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
for (Descriptor d :
objectCodecOptimizer.descriptorGrouper.getMapDescriptors()) {
expressions.add(serializeGroup(Collections.singletonList(d), bean,
buffer, false));
}
+ addGroupExpressions(
+ objectCodecOptimizer.otherWriteGroups, numGroups, expressions, bean,
buffer);
return expressions;
}
@@ -459,14 +459,14 @@ public class ObjectCodecBuilder extends
BaseObjectCodecBuilder {
objectCodecOptimizer.boxedReadGroups, numGroups, expressions, bean,
buffer);
deserializeReadGroup(
objectCodecOptimizer.finalReadGroups, numGroups, expressions, bean,
buffer);
- deserializeReadGroup(
- objectCodecOptimizer.otherReadGroups, numGroups, expressions, bean,
buffer);
for (Descriptor d :
objectCodecOptimizer.descriptorGrouper.getCollectionDescriptors()) {
expressions.add(deserializeGroup(Collections.singletonList(d), bean,
buffer, false));
}
for (Descriptor d :
objectCodecOptimizer.descriptorGrouper.getMapDescriptors()) {
expressions.add(deserializeGroup(Collections.singletonList(d), bean,
buffer, false));
}
+ deserializeReadGroup(
+ objectCodecOptimizer.otherReadGroups, numGroups, expressions, bean,
buffer);
if (isRecord) {
if (recordCtrAccessible) {
assert bean instanceof FieldsCollector;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index 8b9eca8dc..62fda3322 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -1767,13 +1767,14 @@ public class ClassResolver extends TypeResolver {
boolean descriptorsGroupedOrdered,
Function<Descriptor, Descriptor> descriptorUpdator) {
return DescriptorGrouper.createDescriptorGrouper(
- fory.getClassResolver()::isMonomorphic,
- descriptors,
- descriptorsGroupedOrdered,
- descriptorUpdator,
- fory.compressInt(),
- fory.compressLong(),
- DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME);
+ fory.getClassResolver()::isMonomorphic,
+ descriptors,
+ descriptorsGroupedOrdered,
+ descriptorUpdator,
+ fory.compressInt(),
+ fory.compressLong(),
+ DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME)
+ .sort();
}
/**
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index 309e758bd..9d56c4829 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -38,6 +38,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -887,26 +888,43 @@ public class XtypeResolver extends TypeResolver {
boolean descriptorsGroupedOrdered,
Function<Descriptor, Descriptor> descriptorUpdator) {
return DescriptorGrouper.createDescriptorGrouper(
- this::isMonomorphic,
- descriptors,
- descriptorsGroupedOrdered,
- descriptorUpdator,
- fory.compressInt(),
- fory.compressLong(),
- (o1, o2) -> {
- int xtypeId = getXtypeId(o1.getRawType());
- int xtypeId2 = getXtypeId(o2.getRawType());
- if (xtypeId == xtypeId2) {
- return o1.getSnakeCaseName().compareTo(o2.getSnakeCaseName());
- } else {
- return xtypeId - xtypeId2;
- }
- });
- }
-
- private static final int UNKNOWN_TYPE_ID = -1;
+ clz -> {
+ ClassInfo classInfo = getClassInfo(clz, false);
+ if (classInfo == null || clz.isEnum()) {
+ return false;
+ }
+ byte foryTypeId = (byte) (classInfo.xtypeId & 0xff);
+ if (foryTypeId == 0
+ || foryTypeId == Types.UNKNOWN
+ || Types.isUserDefinedType(foryTypeId)) {
+ return false;
+ }
+ return foryTypeId != Types.LIST && foryTypeId != Types.SET &&
foryTypeId != Types.MAP;
+ },
+ descriptors,
+ descriptorsGroupedOrdered,
+ descriptorUpdator,
+ fory.compressInt(),
+ fory.compressLong(),
+ (o1, o2) -> {
+ int xtypeId = getXtypeId(o1.getRawType());
+ int xtypeId2 = getXtypeId(o2.getRawType());
+ if (xtypeId == xtypeId2) {
+ return o1.getSnakeCaseName().compareTo(o2.getSnakeCaseName());
+ } else {
+ return xtypeId - xtypeId2;
+ }
+ })
+
.setOtherDescriptorComparator(Comparator.comparing(Descriptor::getSnakeCaseName))
+ .sort();
+ }
+
+ private static final int UNKNOWN_TYPE_ID = Types.UNKNOWN;
private int getXtypeId(Class<?> cls) {
+ if (isSet(cls)) {
+ return Types.SET;
+ }
if (isCollection(cls)) {
return Types.LIST;
}
@@ -922,6 +940,9 @@ public class XtypeResolver extends TypeResolver {
if (cls.isEnum()) {
return Types.ENUM;
}
+ if (cls.isArray()) {
+ return Types.LIST;
+ }
if (ReflectionUtils.isMonomorphic(cls)) {
throw new UnsupportedOperationException(cls + " is not supported for
xlang serialization");
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java
index 1dc0703a7..930e8b67e 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java
@@ -127,7 +127,13 @@ public abstract class AbstractObjectSerializer<T> extends
Serializer<T> {
SerializationBinding binding, GenericTypeField fieldInfo, MemoryBuffer
buffer) {
Object fieldValue;
boolean nullable = fieldInfo.nullable;
- if (fieldInfo.trackingRef) {
+ if (fieldInfo.genericType.getCls().isEnum()) {
+ if (buffer.readByte() == Fory.NULL_FLAG) {
+ return null;
+ } else {
+ return
fieldInfo.genericType.getSerializer(binding.typeResolver).read(buffer);
+ }
+ } else if (fieldInfo.trackingRef) {
fieldValue = binding.readRef(buffer, fieldInfo);
} else {
binding.preserveRefId(-1);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java
index 7d5272d56..c7f507d72 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java
@@ -89,11 +89,9 @@ public class MetaSharedSerializer<T> extends
AbstractObjectSerializer<T> {
"Class version check should be disabled when compatible mode is
enabled.");
Preconditions.checkArgument(
fory.getConfig().isMetaShareEnabled(), "Meta share must be enabled.");
- boolean xlang = fory.isCrossLanguage();
- Collection<Descriptor> descriptors =
- consolidateFields(
- xlang ? fory.getXtypeResolver() : fory.getClassResolver(), type,
classDef);
- DescriptorGrouper descriptorGrouper =
classResolver.createDescriptorGrouper(descriptors, false);
+ Collection<Descriptor> descriptors =
consolidateFields(fory._getTypeResolver(), type, classDef);
+ DescriptorGrouper descriptorGrouper =
+ fory._getTypeResolver().createDescriptorGrouper(descriptors, false);
// d.getField() may be null if not exists in this class when meta share
enabled.
Tuple3<
Tuple2<ObjectSerializer.FinalTypeField[], boolean[]>,
@@ -212,17 +210,17 @@ public class MetaSharedSerializer<T> extends
AbstractObjectSerializer<T> {
}
}
}
- for (ObjectSerializer.GenericTypeField fieldInfo : otherFields) {
- Object fieldValue =
AbstractObjectSerializer.readOtherFieldValue(binding, fieldInfo, buffer);
+ Generics generics = fory.getGenerics();
+ for (ObjectSerializer.GenericTypeField fieldInfo : containerFields) {
+ Object fieldValue =
+ AbstractObjectSerializer.readContainerFieldValue(binding, generics,
fieldInfo, buffer);
FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
if (fieldAccessor != null) {
fieldAccessor.putObject(obj, fieldValue);
}
}
- Generics generics = fory.getGenerics();
- for (ObjectSerializer.GenericTypeField fieldInfo : containerFields) {
- Object fieldValue =
- AbstractObjectSerializer.readContainerFieldValue(binding, generics,
fieldInfo, buffer);
+ for (ObjectSerializer.GenericTypeField fieldInfo : otherFields) {
+ Object fieldValue =
AbstractObjectSerializer.readOtherFieldValue(binding, fieldInfo, buffer);
FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
if (fieldAccessor != null) {
fieldAccessor.putObject(obj, fieldValue);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/NonexistentClassSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/NonexistentClassSerializers.java
index 2adfe97dc..04fe92e77 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/NonexistentClassSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/NonexistentClassSerializers.java
@@ -19,6 +19,7 @@
package org.apache.fory.serializer;
+import static org.apache.fory.serializer.ObjectSerializer.writeOtherFieldValue;
import static org.apache.fory.serializer.SerializationUtils.getTypeResolver;
import java.util.ArrayList;
@@ -75,6 +76,7 @@ public final class NonexistentClassSerializers {
private final ClassInfoHolder classInfoHolder;
private final LongMap<ClassFieldsInfo> fieldsInfoMap;
private final SerializationBinding binding;
+ private final TypeResolver typeResolver;
public NonexistentClassSerializer(Fory fory, ClassDef classDef) {
super(fory, NonexistentClass.NonexistentMetaShared.class);
@@ -82,6 +84,7 @@ public final class NonexistentClassSerializers {
classInfoHolder = fory.getClassResolver().nilClassInfoHolder();
fieldsInfoMap = new LongMap<>();
binding = SerializationBinding.createBinding(fory);
+ typeResolver = fory._getTypeResolver();
Preconditions.checkArgument(fory.getConfig().isMetaShareEnabled());
}
@@ -140,21 +143,16 @@ public final class NonexistentClassSerializers {
}
}
}
- for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.otherFields) {
- Object fieldValue = value.get(fieldInfo.qualifiedFieldName);
- boolean nullable = fieldInfo.nullable;
- if (fieldInfo.trackingRef) {
- binding.writeRef(buffer, fieldValue, fieldInfo.classInfoHolder);
- } else {
- binding.writeNullable(buffer, fieldValue, fieldInfo.classInfoHolder,
nullable);
- }
- }
Generics generics = fory.getGenerics();
for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.containerFields) {
Object fieldValue = value.get(fieldInfo.qualifiedFieldName);
ObjectSerializer.writeContainerFieldValue(
binding, refResolver, classResolver, generics, fieldInfo, buffer,
fieldValue);
}
+ for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.otherFields) {
+ Object fieldValue = value.get(fieldInfo.qualifiedFieldName);
+ writeOtherFieldValue(binding, typeResolver, buffer, fieldInfo,
fieldValue);
+ }
}
private ClassFieldsInfo getClassFieldsInfo(ClassDef classDef) {
@@ -214,17 +212,17 @@ public final class NonexistentClassSerializers {
}
entries.add(new MapEntry(fieldInfo.qualifiedFieldName, fieldValue));
}
- for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.otherFields) {
- Object fieldValue =
- AbstractObjectSerializer.readOtherFieldValue(binding, fieldInfo,
buffer);
- entries.add(new MapEntry(fieldInfo.qualifiedFieldName, fieldValue));
- }
Generics generics = fory.getGenerics();
for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.containerFields) {
Object fieldValue =
AbstractObjectSerializer.readContainerFieldValue(binding,
generics, fieldInfo, buffer);
entries.add(new MapEntry(fieldInfo.qualifiedFieldName, fieldValue));
}
+ for (ObjectSerializer.GenericTypeField fieldInfo :
fieldsInfo.otherFields) {
+ Object fieldValue =
+ AbstractObjectSerializer.readOtherFieldValue(binding, fieldInfo,
buffer);
+ entries.add(new MapEntry(fieldInfo.qualifiedFieldName, fieldValue));
+ }
obj.setEntries(entries);
return obj;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
index b26ca8ad7..db1cf2dcc 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java
@@ -103,6 +103,7 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
}
DescriptorGrouper descriptorGrouper =
typeResolver.createDescriptorGrouper(descriptors, false);
descriptors = descriptorGrouper.getSortedDescriptors();
+ System.out.println(descriptors.stream().map(f ->
f.getName()).collect(Collectors.toList()));
if (isRecord) {
List<String> fieldNames =
descriptors.stream().map(Descriptor::getName).collect(Collectors.toList());
@@ -132,19 +133,15 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
}
// write order: primitive,boxed,final,other,collection,map
writeFinalFields(buffer, value, fory, refResolver, typeResolver);
- writeOtherFields(buffer, value);
writeContainerFields(buffer, value, fory, refResolver, typeResolver);
+ writeOtherFields(buffer, value);
}
private void writeOtherFields(MemoryBuffer buffer, T value) {
for (GenericTypeField fieldInfo : otherFields) {
FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
Object fieldValue = fieldAccessor.getObject(value);
- if (fieldInfo.trackingRef) {
- binding.writeRef(buffer, fieldValue, fieldInfo.classInfoHolder);
- } else {
- binding.writeNullable(buffer, fieldValue, fieldInfo.classInfoHolder,
fieldInfo.nullable);
- }
+ writeOtherFieldValue(binding, typeResolver, buffer, fieldInfo,
fieldValue);
}
}
@@ -239,6 +236,24 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
}
}
+ static void writeOtherFieldValue(
+ SerializationBinding binding,
+ TypeResolver typeResolver,
+ MemoryBuffer buffer,
+ GenericTypeField fieldInfo,
+ Object fieldValue) {
+ if (fieldValue == null) {
+ buffer.writeByte(Fory.NULL_FLAG);
+ } else if (fieldValue.getClass().isEnum()) {
+ buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG);
+ fieldInfo.genericType.getSerializer(typeResolver).write(buffer,
fieldValue);
+ } else if (fieldInfo.trackingRef) {
+ binding.writeRef(buffer, fieldValue, fieldInfo.classInfoHolder);
+ } else {
+ binding.writeNullable(buffer, fieldValue, fieldInfo.classInfoHolder,
fieldInfo.nullable);
+ }
+ }
+
@Override
public T read(MemoryBuffer buffer) {
if (isRecord) {
@@ -286,15 +301,15 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
fieldValues[counter++] = fieldValue;
}
}
- for (GenericTypeField fieldInfo : otherFields) {
- Object fieldValue = readOtherFieldValue(binding, fieldInfo, buffer);
- fieldValues[counter++] = fieldValue;
- }
Generics generics = fory.getGenerics();
for (GenericTypeField fieldInfo : containerFields) {
Object fieldValue = readContainerFieldValue(binding, generics,
fieldInfo, buffer);
fieldValues[counter++] = fieldValue;
}
+ for (GenericTypeField fieldInfo : otherFields) {
+ Object fieldValue = readOtherFieldValue(binding, fieldInfo, buffer);
+ fieldValues[counter++] = fieldValue;
+ }
return fieldValues;
}
@@ -325,17 +340,17 @@ public final class ObjectSerializer<T> extends
AbstractObjectSerializer<T> {
fieldAccessor.putObject(obj, fieldValue);
}
}
- for (GenericTypeField fieldInfo : otherFields) {
- Object fieldValue = readOtherFieldValue(binding, fieldInfo, buffer);
- FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
- fieldAccessor.putObject(obj, fieldValue);
- }
Generics generics = fory.getGenerics();
for (GenericTypeField fieldInfo : containerFields) {
Object fieldValue = readContainerFieldValue(binding, generics,
fieldInfo, buffer);
FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
fieldAccessor.putObject(obj, fieldValue);
}
+ for (GenericTypeField fieldInfo : otherFields) {
+ Object fieldValue = readOtherFieldValue(binding, fieldInfo, buffer);
+ FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
+ fieldAccessor.putObject(obj, fieldValue);
+ }
return obj;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializationBinding.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializationBinding.java
index 087189049..3fb1c8417 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializationBinding.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializationBinding.java
@@ -28,6 +28,7 @@ import org.apache.fory.resolver.ClassInfo;
import org.apache.fory.resolver.ClassInfoHolder;
import org.apache.fory.resolver.ClassResolver;
import org.apache.fory.resolver.RefResolver;
+import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.resolver.XtypeResolver;
// This polymorphic interface has cost, do not expose it as a public class
@@ -37,10 +38,12 @@ import org.apache.fory.resolver.XtypeResolver;
abstract class SerializationBinding {
protected final Fory fory;
protected final RefResolver refResolver;
+ protected final TypeResolver typeResolver;
SerializationBinding(Fory fory) {
this.fory = fory;
this.refResolver = fory.getRefResolver();
+ typeResolver = fory._getTypeResolver();
}
abstract <T> void writeRef(MemoryBuffer buffer, T obj);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/type/DescriptorGrouper.java
b/java/fory-core/src/main/java/org/apache/fory/type/DescriptorGrouper.java
index aa1b4cbb5..95213b893 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/DescriptorGrouper.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/DescriptorGrouper.java
@@ -29,6 +29,7 @@ import java.util.List;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
+import org.apache.fory.util.Preconditions;
import org.apache.fory.util.record.RecordUtils;
/**
@@ -55,6 +56,11 @@ public class DescriptorGrouper {
}
return c;
};
+ private final Collection<Descriptor> descriptors;
+ private final Predicate<Class<?>> isMonomorphic;
+ private final Function<Descriptor, Descriptor> descriptorUpdater;
+ private final boolean descriptorsGroupedOrdered;
+ private boolean sorted = false;
/**
* When compress disabled, sort primitive descriptors from largest to
smallest, if size is the
@@ -138,7 +144,7 @@ public class DescriptorGrouper {
// The key/value type should be final.
private final Collection<Descriptor> mapDescriptors;
private final Collection<Descriptor> finalDescriptors;
- private final Collection<Descriptor> otherDescriptors;
+ private Collection<Descriptor> otherDescriptors;
/**
* Create a descriptor grouper.
@@ -157,6 +163,10 @@ public class DescriptorGrouper {
Function<Descriptor, Descriptor> descriptorUpdater,
Comparator<Descriptor> primitiveComparator,
Comparator<Descriptor> comparator) {
+ this.descriptors = descriptors;
+ this.isMonomorphic = isMonomorphic;
+ this.descriptorUpdater = descriptorUpdater;
+ this.descriptorsGroupedOrdered = descriptorsGroupedOrdered;
this.primitiveDescriptors =
descriptorsGroupedOrdered ? new ArrayList<>() : new
TreeSet<>(primitiveComparator);
this.boxedDescriptors =
@@ -168,6 +178,19 @@ public class DescriptorGrouper {
descriptorsGroupedOrdered ? new ArrayList<>() : new
TreeSet<>(comparator);
this.otherDescriptors =
descriptorsGroupedOrdered ? new ArrayList<>() : new
TreeSet<>(comparator);
+ }
+
+ public DescriptorGrouper setOtherDescriptorComparator(Comparator<Descriptor>
comparator) {
+ Preconditions.checkArgument(!sorted);
+ this.otherDescriptors =
+ descriptorsGroupedOrdered ? new ArrayList<>() : new
TreeSet<>(comparator);
+ return this;
+ }
+
+ public DescriptorGrouper sort() {
+ if (sorted) {
+ return this;
+ }
for (Descriptor descriptor : descriptors) {
if (TypeUtils.isPrimitive(descriptor.getRawType())) {
primitiveDescriptors.add(descriptorUpdater.apply(descriptor));
@@ -183,40 +206,49 @@ public class DescriptorGrouper {
otherDescriptors.add(descriptorUpdater.apply(descriptor));
}
}
+ sorted = true;
+ return this;
}
public List<Descriptor> getSortedDescriptors() {
+ Preconditions.checkArgument(sorted);
List<Descriptor> descriptors = new ArrayList<>(getNumDescriptors());
descriptors.addAll(getPrimitiveDescriptors());
descriptors.addAll(getBoxedDescriptors());
descriptors.addAll(getFinalDescriptors());
- descriptors.addAll(getOtherDescriptors());
descriptors.addAll(getCollectionDescriptors());
descriptors.addAll(getMapDescriptors());
+ descriptors.addAll(getOtherDescriptors());
return descriptors;
}
public Collection<Descriptor> getPrimitiveDescriptors() {
+ Preconditions.checkArgument(sorted);
return primitiveDescriptors;
}
public Collection<Descriptor> getBoxedDescriptors() {
+ Preconditions.checkArgument(sorted);
return boxedDescriptors;
}
public Collection<Descriptor> getCollectionDescriptors() {
+ Preconditions.checkArgument(sorted);
return collectionDescriptors;
}
public Collection<Descriptor> getMapDescriptors() {
+ Preconditions.checkArgument(sorted);
return mapDescriptors;
}
public Collection<Descriptor> getFinalDescriptors() {
+ Preconditions.checkArgument(sorted);
return finalDescriptors;
}
public Collection<Descriptor> getOtherDescriptors() {
+ Preconditions.checkArgument(sorted);
return otherDescriptors;
}
@@ -250,6 +282,7 @@ public class DescriptorGrouper {
}
public int getNumDescriptors() {
+ Preconditions.checkArgument(sorted);
return primitiveDescriptors.size()
+ boxedDescriptors.size()
+ collectionDescriptors.size()
diff --git a/java/fory-core/src/main/java/org/apache/fory/type/GenericType.java
b/java/fory-core/src/main/java/org/apache/fory/type/GenericType.java
index 36c9a3eb7..9e8263da1 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/GenericType.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/GenericType.java
@@ -194,7 +194,7 @@ public class GenericType {
this.serializer = serializer;
}
- public Serializer<?> getSerializer(TypeResolver classResolver) {
+ public Serializer getSerializer(TypeResolver classResolver) {
Serializer<?> serializer = this.serializer;
if (serializer == null) {
serializer = classResolver.getSerializer(cls);
diff --git
a/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java
b/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java
index 7d6c28eae..99277da4c 100644
---
a/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java
@@ -150,13 +150,14 @@ public class DescriptorGrouperTest {
new Descriptor(new TypeRef<Map<String, String>>() {}, "c" + index++,
-1, "TestClass"));
DescriptorGrouper grouper =
DescriptorGrouper.createDescriptorGrouper(
- ReflectionUtils::isMonomorphic,
- descriptors,
- false,
- null,
- false,
- false,
- DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME);
+ ReflectionUtils::isMonomorphic,
+ descriptors,
+ false,
+ null,
+ false,
+ false,
+ DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME)
+ .sort();
{
List<? extends Class<?>> classes =
grouper.getPrimitiveDescriptors().stream()
@@ -232,13 +233,14 @@ public class DescriptorGrouperTest {
public void testCompressedPrimitiveGrouper() {
DescriptorGrouper grouper =
DescriptorGrouper.createDescriptorGrouper(
- ReflectionUtils::isMonomorphic,
- createDescriptors(),
- false,
- null,
- true,
- true,
- DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME);
+ ReflectionUtils::isMonomorphic,
+ createDescriptors(),
+ false,
+ null,
+ true,
+ true,
+ DescriptorGrouper.COMPARATOR_BY_TYPE_AND_NAME)
+ .sort();
{
List<? extends Class<?>> classes =
grouper.getPrimitiveDescriptors().stream()
diff --git a/python/pyfory/_struct.py b/python/pyfory/_struct.py
index 63da403ce..2b9ae8714 100644
--- a/python/pyfory/_struct.py
+++ b/python/pyfory/_struct.py
@@ -39,7 +39,7 @@ from pyfory.type import (
is_list_type,
is_map_type,
get_primitive_type_size,
- is_primitive_array_type,
+ is_polymorphic_type,
)
from pyfory.type import is_subclass
@@ -79,6 +79,13 @@ class StructFieldSerializerVisitor(TypeVisitor):
elem_serializer = infer_field("item", elem_type, self,
types_path=types_path)
return ListSerializer(self.fory, list, elem_serializer)
+ def visit_set(self, field_name, elem_type, types_path=None):
+ from pyfory.serializer import SetSerializer # Local import
+
+ # Infer type recursively for type such as Set[Dict[str, str]]
+ elem_serializer = infer_field("item", elem_type, self,
types_path=types_path)
+ return SetSerializer(self.fory, set, elem_serializer)
+
def visit_dict(self, field_name, key_type, value_type, types_path=None):
from pyfory.serializer import MapSerializer # Local import
@@ -117,8 +124,9 @@ _time_types = {datetime.date, datetime.datetime,
datetime.timedelta}
def _sort_fields(type_resolver, field_names, serializers):
boxed_types = []
collection_types = []
+ set_types = []
map_types = []
- final_types = []
+ internal_types = []
other_types = []
type_ids = []
for field_name, serializer in zip(field_names, serializers):
@@ -127,7 +135,7 @@ def _sort_fields(type_resolver, field_names, serializers):
else:
type_ids.append(
(
- type_resolver.get_typeinfo(serializer.type_).type_id,
+ type_resolver.get_typeinfo(serializer.type_).type_id &
0xFF,
serializer,
field_name,
)
@@ -135,16 +143,21 @@ def _sort_fields(type_resolver, field_names, serializers):
for type_id, serializer, field_name in type_ids:
if is_primitive_type(type_id):
container = boxed_types
+ elif type_id == TypeId.SET:
+ container = set_types
elif is_list_type(serializer.type_):
container = collection_types
elif is_map_type(serializer.type_):
container = map_types
- elif (
- type_id in {TypeId.STRING} or is_primitive_array_type(type_id) or
is_subclass(serializer.type_, enum.Enum)
- ) or serializer.type_ in _time_types:
- container = final_types
- else:
+ elif is_polymorphic_type(type_id) or type_id in {
+ TypeId.ENUM,
+ TypeId.NAMED_ENUM,
+ }:
container = other_types
+ else:
+ assert TypeId.LOWER_BOUND < type_id < TypeId.UNKNOWN, (type_id,)
+ assert type_id != TypeId.UNKNOWN, serializer
+ container = internal_types
container.append((type_id, serializer, field_name))
def sorter(item):
@@ -162,10 +175,10 @@ def _sort_fields(type_resolver, field_names, serializers):
boxed_types = sorted(boxed_types, key=numeric_sorter)
collection_types = sorted(collection_types, key=sorter)
- final_types = sorted(final_types, key=sorter)
+ internal_types = sorted(internal_types, key=sorter)
map_types = sorted(map_types, key=sorter)
- other_types = sorted(other_types, key=sorter)
- all_types = boxed_types + final_types + other_types + collection_types +
map_types
+ other_types = sorted(other_types, key=lambda item: item[2])
+ all_types = boxed_types + internal_types + collection_types + set_types +
map_types + other_types
return [t[2] for t in all_types], [t[1] for t in all_types]
@@ -182,6 +195,11 @@ class StructHashVisitor(TypeVisitor):
xtype_id = self.fory.type_resolver.get_typeinfo(list).type_id
self._hash = self._compute_field_hash(self._hash, abs(xtype_id))
+ def visit_set(self, field_name, elem_type, types_path=None):
+ # TODO add set element type to hash.
+ xtype_id = self.fory.type_resolver.get_typeinfo(set).type_id
+ self._hash = self._compute_field_hash(self._hash, abs(xtype_id))
+
def visit_dict(self, field_name, key_type, value_type, types_path=None):
# TODO add map key/value type to hash.
xtype_id = self.fory.type_resolver.get_typeinfo(dict).type_id
@@ -237,6 +255,11 @@ class StructTypeIdVisitor(TypeVisitor):
elem_ids = infer_field("item", elem_type, self, types_path=types_path)
return TypeId.LIST, elem_ids
+ def visit_set(self, field_name, elem_type, types_path=None):
+ # Infer type recursively for type such as Set[Dict[str, str]]
+ elem_ids = infer_field("item", elem_type, self, types_path=types_path)
+ return TypeId.SET, elem_ids
+
def visit_dict(self, field_name, key_type, value_type, types_path=None):
# Infer type recursively for type such as Dict[str, Dict[str, str]]
key_ids = infer_field("key", key_type, self, types_path=types_path)
@@ -267,6 +290,11 @@ class StructTypeVisitor(TypeVisitor):
elem_types = infer_field("item", elem_type, self,
types_path=types_path)
return typing.List, elem_types
+ def visit_set(self, field_name, elem_type, types_path=None):
+ # Infer type recursively for type such as Set[Dict[str, str]]
+ elem_types = infer_field("item", elem_type, self,
types_path=types_path)
+ return typing.Set, elem_types
+
def visit_dict(self, field_name, key_type, value_type, types_path=None):
# Infer type recursively for type such as Dict[str, Dict[str, str]]
key_types = infer_field("key", key_type, self, types_path=types_path)
diff --git a/python/pyfory/format/infer.py b/python/pyfory/format/infer.py
index 18410345f..a9596b549 100644
--- a/python/pyfory/format/infer.py
+++ b/python/pyfory/format/infer.py
@@ -109,6 +109,11 @@ class ArrowTypeVisitor(TypeVisitor):
elem_field = infer_field("item", elem_type, self,
types_path=types_path)
return pa.field(field_name, pa.list_(elem_field.type))
+ def visit_set(self, field_name, elem_type, types_path=None):
+ # Infer type recursively for type such as Set[Dict[str, str]]
+ elem_field = infer_field("item", elem_type, self,
types_path=types_path)
+ return pa.field(field_name, pa.list_(elem_field.type))
+
def visit_dict(self, field_name, key_type, value_type, types_path=None):
# Infer type recursively for type such as Dict[str, Dict[str, str]]
key_field = infer_field("key", key_type, self, types_path=types_path)
diff --git a/python/pyfory/tests/test_struct.py
b/python/pyfory/tests/test_struct.py
index 955c16fcc..2e522964a 100644
--- a/python/pyfory/tests/test_struct.py
+++ b/python/pyfory/tests/test_struct.py
@@ -16,7 +16,8 @@
# under the License.
from dataclasses import dataclass
-from typing import Dict, Any, List
+import datetime
+from typing import Dict, Any, List, Set
import os
import pytest
@@ -137,6 +138,46 @@ class DataClassObject:
)
+def test_sort_fields():
+ @dataclass
+ class TestClass:
+ f1: pyfory.Int32Type
+ f2: List[pyfory.Int16Type]
+ f3: Dict[str, pyfory.Float64Type]
+ f4: str
+ f5: pyfory.Float32Type
+ f6: bytes
+ f7: bool
+ f8: Any
+ f9: Dict[pyfory.Int32Type, pyfory.Float64Type]
+ f10: List[str]
+ f11: pyfory.Int8Type
+ f12: pyfory.Int64Type
+ f13: pyfory.Float64Type
+ f14: Set[pyfory.Int32Type]
+ f15: datetime.datetime
+
+ fory = Fory(xlang=True, ref=True)
+ serializer = DataClassSerializer(fory, TestClass, xlang=True)
+ assert serializer._field_names == [
+ "f13",
+ "f5",
+ "f11",
+ "f7",
+ "f12",
+ "f1",
+ "f4",
+ "f15",
+ "f6",
+ "f10",
+ "f2",
+ "f14",
+ "f3",
+ "f9",
+ "f8",
+ ]
+
+
def test_data_class_serializer_xlang():
fory = Fory(xlang=True, ref=True)
fory.register_type(ComplexObject, typename="example.ComplexObject")
diff --git a/python/pyfory/type.py b/python/pyfory/type.py
index e596c4678..dc25dd6b6 100644
--- a/python/pyfory/type.py
+++ b/python/pyfory/type.py
@@ -130,6 +130,7 @@ class TypeId:
See `org.apache.fory.types.Type`
"""
+ LOWER_BOUND = 0
# null value
NA = 0
# a boolean value (true or false).
@@ -397,6 +398,10 @@ class TypeVisitor(ABC):
def visit_list(self, field_name, elem_type, types_path=None):
pass
+ @abstractmethod
+ def visit_set(self, field_name, elem_type, types_path=None):
+ pass
+
@abstractmethod
def visit_dict(self, field_name, key_type, value_type, types_path=None):
pass
@@ -428,6 +433,9 @@ def infer_field(field_name, type_, visitor: TypeVisitor,
types_path=None):
if origin is list or origin == typing.List:
elem_type = args[0]
return visitor.visit_list(field_name, elem_type,
types_path=types_path)
+ elif origin is set or origin == typing.Set:
+ elem_type = args[0]
+ return visitor.visit_set(field_name, elem_type,
types_path=types_path)
elif origin is dict or origin == typing.Dict:
key_type, value_type = args
return visitor.visit_dict(field_name, key_type, value_type,
types_path=types_path)
diff --git a/rust/fory-core/src/buffer.rs b/rust/fory-core/src/buffer.rs
index ec5ea2294..9baf94c65 100644
--- a/rust/fory-core/src/buffer.rs
+++ b/rust/fory-core/src/buffer.rs
@@ -29,23 +29,28 @@ pub struct Writer {
}
impl Writer {
+ #[inline(always)]
pub fn reset(&mut self) {
// keep capacity and reset len to 0
self.bf.clear();
}
+ #[inline(always)]
pub fn dump(&self) -> Vec<u8> {
self.bf.clone()
}
+ #[inline(always)]
pub fn len(&self) -> usize {
self.bf.len()
}
+ #[inline(always)]
pub fn is_empty(&self) -> bool {
self.bf.is_empty()
}
+ #[inline(always)]
pub fn reserve(&mut self, additional: usize) {
self.reserved += additional;
if self.bf.capacity() < self.reserved {
@@ -53,10 +58,12 @@ impl Writer {
}
}
+ #[inline(always)]
pub fn skip(&mut self, len: usize) {
self.bf.resize(self.bf.len() + len, 0);
}
+ #[inline(always)]
pub fn set_bytes(&mut self, offset: usize, data: &[u8]) {
self.bf
.get_mut(offset..offset + data.len())
@@ -64,61 +71,75 @@ impl Writer {
.copy_from_slice(data);
}
+ #[inline(always)]
pub fn write_bytes(&mut self, v: &[u8]) -> usize {
self.reserve(v.len());
self.bf.extend_from_slice(v);
v.len()
}
+ #[inline(always)]
pub fn write_u8(&mut self, value: u8) {
self.bf.write_u8(value).unwrap();
}
+ #[inline(always)]
pub fn write_i8(&mut self, value: i8) {
self.bf.write_i8(value).unwrap();
}
+ #[inline(always)]
pub fn write_u16(&mut self, value: u16) {
self.bf.write_u16::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_i16(&mut self, value: i16) {
self.bf.write_i16::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_u32(&mut self, value: u32) {
self.bf.write_u32::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_i32(&mut self, value: i32) {
self.bf.write_i32::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_f32(&mut self, value: f32) {
self.bf.write_f32::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_i64(&mut self, value: i64) {
self.bf.write_i64::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_f64(&mut self, value: f64) {
self.bf.write_f64::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_u64(&mut self, value: u64) {
self.bf.write_u64::<LittleEndian>(value).unwrap();
}
+ #[inline(always)]
pub fn write_varint32(&mut self, value: i32) {
let zigzag = ((value as i64) << 1) ^ ((value as i64) >> 31);
self._write_varuint32(zigzag as u32)
}
+ #[inline(always)]
pub fn write_varuint32(&mut self, value: u32) {
self._write_varuint32(value)
}
+ #[inline(always)]
fn _write_varuint32(&mut self, value: u32) {
if value < 0x80 {
self.write_u8(value as u8);
@@ -157,15 +178,18 @@ impl Writer {
}
}
+ #[inline(always)]
pub fn write_varint64(&mut self, value: i64) {
let zigzag = ((value << 1) ^ (value >> 63)) as u64;
self._write_varuint64(zigzag)
}
+ #[inline(always)]
pub fn write_varuint64(&mut self, value: u64) {
self._write_varuint64(value)
}
+ #[inline(always)]
fn _write_varuint64(&mut self, value: u64) {
if value < 0x80 {
self.write_u8(value as u8);
@@ -264,6 +288,7 @@ impl Writer {
}
}
+ #[inline(always)]
pub fn write_varuint36_small(&mut self, value: u64) {
assert!(value < (1u64 << 36), "value too large for 36-bit varint");
if value < 0x80 {
@@ -297,14 +322,17 @@ impl Writer {
}
}
+ #[inline(always)]
pub fn write_latin1_string(&mut self, s: &str) {
write_latin1_simd(self, s);
}
+ #[inline(always)]
pub fn write_utf8_string(&mut self, s: &str) {
write_utf8_simd(self, s);
}
+ #[inline(always)]
pub fn write_utf16_bytes(&mut self, bytes: &[u16]) {
write_utf16_simd(self, bytes);
}
@@ -317,6 +345,7 @@ pub struct Reader {
}
impl Reader {
+ #[inline(always)]
pub fn new(bf: &[u8]) -> Reader {
Reader {
bf: bf.as_ptr(),
@@ -325,98 +354,114 @@ impl Reader {
}
}
+ #[inline(always)]
pub fn init(&mut self, bf: &[u8]) {
self.bf = bf.as_ptr();
self.len = bf.len();
self.cursor = 0;
}
+ #[inline(always)]
pub fn reset(&mut self) {
self.bf = std::ptr::null();
self.len = 0;
self.cursor = 0;
}
+ #[inline(always)]
pub(crate) fn move_next(&mut self, additional: usize) {
self.cursor += additional;
}
- #[inline]
+ #[inline(always)]
unsafe fn ptr_at(&self, offset: usize) -> *const u8 {
self.bf.add(offset)
}
+ #[inline(always)]
pub fn slice_after_cursor(&self) -> &[u8] {
- let remaining = self.len - self.cursor;
- if self.bf.is_null() || remaining == 0 {
+ if self.bf.is_null() || self.cursor >= self.len {
&[]
} else {
+ let remaining = self.len - self.cursor;
unsafe { std::slice::from_raw_parts(self.bf.add(self.cursor),
remaining) }
}
}
+ #[inline(always)]
pub fn get_cursor(&self) -> usize {
self.cursor
}
+ #[inline(always)]
pub fn read_u8(&mut self) -> u8 {
let result = unsafe { *self.ptr_at(self.cursor) };
self.move_next(1);
result
}
+ #[inline(always)]
pub fn read_i8(&mut self) -> i8 {
self.read_u8() as i8
}
+ #[inline(always)]
pub fn read_u16(&mut self) -> u16 {
let result = LittleEndian::read_u16(self.slice_after_cursor());
self.move_next(2);
result
}
+ #[inline(always)]
pub fn read_i16(&mut self) -> i16 {
let result = LittleEndian::read_i16(self.slice_after_cursor());
self.move_next(2);
result
}
+ #[inline(always)]
pub fn read_u32(&mut self) -> u32 {
let result = LittleEndian::read_u32(self.slice_after_cursor());
self.move_next(4);
result
}
+ #[inline(always)]
pub fn read_i32(&mut self) -> i32 {
let result = LittleEndian::read_i32(self.slice_after_cursor());
self.move_next(4);
result
}
+ #[inline(always)]
pub fn read_u64(&mut self) -> u64 {
let result = LittleEndian::read_u64(self.slice_after_cursor());
self.move_next(8);
result
}
+ #[inline(always)]
pub fn read_i64(&mut self) -> i64 {
let result = LittleEndian::read_i64(self.slice_after_cursor());
self.move_next(8);
result
}
+ #[inline(always)]
pub fn read_f32(&mut self) -> f32 {
let result = LittleEndian::read_f32(self.slice_after_cursor());
self.move_next(4);
result
}
+ #[inline(always)]
pub fn read_f64(&mut self) -> f64 {
let result = LittleEndian::read_f64(self.slice_after_cursor());
self.move_next(8);
result
}
+ #[inline(always)]
pub fn read_varuint32(&mut self) -> u32 {
let start = self.cursor;
let b0 = unsafe { *self.bf.add(start) as u32 };
@@ -453,11 +498,13 @@ impl Reader {
encoded
}
+ #[inline(always)]
pub fn read_varint32(&mut self) -> i32 {
let encoded = self.read_varuint32();
((encoded >> 1) as i32) ^ -((encoded & 1) as i32)
}
+ #[inline(always)]
pub fn read_varuint64(&mut self) -> u64 {
let start = self.cursor;
let b0 = unsafe { *self.bf.add(start) } as u64;
@@ -522,23 +569,28 @@ impl Reader {
var64
}
+ #[inline(always)]
pub fn read_varint64(&mut self) -> i64 {
let encoded = self.read_varuint64();
((encoded >> 1) as i64) ^ -((encoded & 1) as i64)
}
+ #[inline(always)]
pub fn read_latin1_string(&mut self, len: usize) -> String {
read_latin1_simd(self, len)
}
+ #[inline(always)]
pub fn read_utf8_string(&mut self, len: usize) -> String {
read_utf8_simd(self, len)
}
+ #[inline(always)]
pub fn read_utf16_string(&mut self, len: usize) -> String {
read_utf16_simd(self, len)
}
+ #[inline(always)]
pub fn read_varuint36small(&mut self) -> u64 {
let start = self.cursor;
// fast path
@@ -580,10 +632,12 @@ impl Reader {
result
}
+ #[inline(always)]
pub fn skip(&mut self, len: u32) {
self.move_next(len as usize);
}
+ #[inline(always)]
pub fn get_slice(&self) -> &[u8] {
if self.bf.is_null() || self.len == 0 {
&[]
@@ -592,12 +646,14 @@ impl Reader {
}
}
+ #[inline(always)]
pub fn read_bytes(&mut self, len: usize) -> &[u8] {
let s = unsafe { slice::from_raw_parts(self.bf.add(self.cursor), len)
};
self.move_next(len);
s
}
+ #[inline(always)]
pub fn reset_cursor_to_here(&self) -> impl FnOnce(&mut Self) {
let raw_cursor = self.cursor;
move |this: &mut Self| {
@@ -605,6 +661,7 @@ impl Reader {
}
}
+ #[inline(always)]
pub fn aligned<T>(&self) -> bool {
if self.bf.is_null() {
return false;
diff --git a/rust/fory-core/src/meta/type_meta.rs
b/rust/fory-core/src/meta/type_meta.rs
index 2d56a2192..0ae8e6c5a 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -21,7 +21,7 @@ use crate::meta::{
murmurhash3_x64_128, Encoding, MetaString, MetaStringDecoder,
FIELD_NAME_DECODER,
FIELD_NAME_ENCODER, NAMESPACE_DECODER, TYPE_NAME_DECODER,
};
-use crate::types::{TypeId, FINAL_TYPES, PRIMITIVE_ARRAY_TYPES,
PRIMITIVE_TYPES};
+use crate::types::{TypeId, PRIMITIVE_TYPES};
use anyhow::anyhow;
use std::clone::Clone;
use std::cmp::min;
@@ -181,6 +181,7 @@ impl FieldType {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FieldInfo {
+ pub field_id: i16,
pub field_name: String,
pub field_type: FieldType,
}
@@ -188,6 +189,7 @@ pub struct FieldInfo {
impl FieldInfo {
pub fn new(field_name: &str, field_type: FieldType) -> FieldInfo {
FieldInfo {
+ field_id: -1i16,
field_name: field_name.to_string(),
field_type,
}
@@ -223,6 +225,7 @@ impl FieldInfo {
.decode(field_name_bytes, encoding)
.unwrap();
FieldInfo {
+ field_id: -1i16,
field_name: field_name.original,
field_type,
}
@@ -386,15 +389,16 @@ impl TypeMetaLayer {
// group
let mut primitive_fields = Vec::new();
let mut nullable_primitive_fields = Vec::new();
- let mut final_fields = Vec::new();
- let mut other_fields = Vec::new();
- let mut unknown_fields = Vec::new();
- let mut collection_fields = Vec::new();
+ let mut internal_type_fields = Vec::new();
+ let mut list_fields = Vec::new();
+ let mut set_fields = Vec::new();
let mut map_fields = Vec::new();
+ let mut other_fields = Vec::new();
for field_info in field_infos.into_iter() {
let mut type_id = field_info.field_type.type_id;
- if type_id == TypeId::ForyNullable as u32 {
+ let is_nullable = type_id == TypeId::ForyNullable as u32;
+ if is_nullable {
type_id =
field_info.field_type.generics.first().unwrap().type_id;
if PRIMITIVE_TYPES.contains(&type_id) {
nullable_primitive_fields.push(field_info);
@@ -402,49 +406,21 @@ impl TypeMetaLayer {
}
}
- let internal_id = type_id & 0xff;
if PRIMITIVE_TYPES.contains(&type_id) {
primitive_fields.push(field_info);
- } else if PRIMITIVE_ARRAY_TYPES.contains(&type_id)
- || FINAL_TYPES.contains(&type_id)
- || [TypeId::ENUM as u32, TypeId::NAMED_ENUM as
u32].contains(&internal_id)
- {
- final_fields.push(field_info);
- } else if [TypeId::LIST as u32, TypeId::SET as
u32].contains(&type_id) {
- collection_fields.push(field_info);
+ } else if TypeId::LIST as u32 == type_id {
+ list_fields.push(field_info);
+ } else if TypeId::SET as u32 == type_id {
+ set_fields.push(field_info);
} else if TypeId::MAP as u32 == type_id {
map_fields.push(field_info);
- } else if [
- TypeId::COMPATIBLE_STRUCT as u32,
- TypeId::NAMED_COMPATIBLE_STRUCT as u32,
- TypeId::EXT as u32,
- TypeId::NAMED_EXT as u32,
- ]
- .contains(&internal_id)
- {
- other_fields.push(field_info);
- } else if internal_id == TypeId::UNKNOWN as u32 {
- unknown_fields.push(field_info);
+ } else if crate::types::is_internal_type(type_id) {
+ internal_type_fields.push(field_info);
} else {
- unreachable!("type_id: {type_id}");
+ other_fields.push(field_info);
}
}
- fn sorter(a: &FieldInfo, b: &FieldInfo) -> std::cmp::Ordering {
- let a_id = if a.field_type.type_id == TypeId::ForyNullable as u32 {
- a.field_type.generics.first().unwrap().type_id
- } else {
- a.field_type.type_id
- };
- let b_id = if b.field_type.type_id == TypeId::ForyNullable as u32 {
- b.field_type.generics.first().unwrap().type_id
- } else {
- b.field_type.type_id
- };
- let a_field_name = &a.field_name;
- let b_field_name = &b.field_name;
- a_id.cmp(&b_id).then_with(|| a_field_name.cmp(b_field_name))
- }
fn get_primitive_type_size(type_id_num: u32) -> i32 {
let type_id = TypeId::try_from(type_id_num as i16).unwrap();
match type_id {
@@ -491,23 +467,33 @@ impl TypeMetaLayer {
compress_a
.cmp(&compress_b)
.then_with(|| size_b.cmp(&size_a))
+ .then_with(|| a_id.cmp(&b_id))
.then_with(|| a_field_name.cmp(b_field_name))
}
+ fn type_then_name_sorter(a: &FieldInfo, b: &FieldInfo) ->
std::cmp::Ordering {
+ a.field_type
+ .type_id
+ .cmp(&b.field_type.type_id)
+ .then_with(|| a.field_name.cmp(&b.field_name))
+ }
+ fn name_sorter(a: &FieldInfo, b: &FieldInfo) -> std::cmp::Ordering {
+ a.field_name.cmp(&b.field_name)
+ }
primitive_fields.sort_by(numeric_sorter);
nullable_primitive_fields.sort_by(numeric_sorter);
- final_fields.sort_by(sorter);
- other_fields.sort_by(sorter);
- unknown_fields.sort_by(sorter);
- collection_fields.sort_by(sorter);
- map_fields.sort_by(sorter);
+ internal_type_fields.sort_by(type_then_name_sorter);
+ list_fields.sort_by(name_sorter);
+ set_fields.sort_by(name_sorter);
+ map_fields.sort_by(name_sorter);
+ other_fields.sort_by(name_sorter);
let mut sorted_field_infos = Vec::with_capacity(fields_len);
sorted_field_infos.extend(primitive_fields);
sorted_field_infos.extend(nullable_primitive_fields);
- sorted_field_infos.extend(final_fields);
- sorted_field_infos.extend(other_fields);
- sorted_field_infos.extend(unknown_fields);
- sorted_field_infos.extend(collection_fields);
+ sorted_field_infos.extend(internal_type_fields);
+ sorted_field_infos.extend(list_fields);
+ sorted_field_infos.extend(set_fields);
sorted_field_infos.extend(map_fields);
+ sorted_field_infos.extend(other_fields);
sorted_field_infos
}
@@ -550,16 +536,16 @@ impl TypeMetaLayer {
pub struct TypeMeta {
// assigned valid value and used, only during deserializing
hash: i64,
- layers: Vec<TypeMetaLayer>,
+ layer: TypeMetaLayer,
}
impl TypeMeta {
pub fn get_field_infos(&self) -> &Vec<FieldInfo> {
- self.layers.first().unwrap().get_field_infos()
+ self.layer.get_field_infos()
}
pub fn get_type_id(&self) -> u32 {
- self.layers.first().unwrap().get_type_id()
+ self.layer.get_type_id()
}
pub fn get_hash(&self) -> i64 {
@@ -567,11 +553,11 @@ impl TypeMeta {
}
pub fn get_type_name(&self) -> MetaString {
- self.layers.first().unwrap().get_type_name().clone()
+ self.layer.get_type_name().clone()
}
pub fn get_namespace(&self) -> MetaString {
- self.layers.first().unwrap().get_namespace().clone()
+ self.layer.get_namespace().clone()
}
pub fn from_fields(
@@ -583,13 +569,7 @@ impl TypeMeta {
) -> TypeMeta {
TypeMeta {
hash: 0,
- layers: vec![TypeMetaLayer::new(
- type_id,
- namespace,
- type_name,
- register_by_name,
- field_infos,
- )],
+ layer: TypeMetaLayer::new(type_id, namespace, type_name,
register_by_name, field_infos),
}
}
#[allow(unused_assignments)]
@@ -604,13 +584,11 @@ impl TypeMeta {
// let is_compressed: bool = (header & COMPRESS_META_FLAG) != 0;
// let meta_hash = header >> (64 - NUM_HASH_BITS);
- let mut layers = Vec::new();
// let current_meta_size = 0;
// while current_meta_size < meta_size {}
let layer = TypeMetaLayer::from_bytes(reader);
- layers.push(layer);
TypeMeta {
- layers,
+ layer,
hash: header,
}
}
@@ -622,7 +600,7 @@ impl TypeMeta {
// for layer in self.layers.iter() {
// layers_writer.bytes(layer.to_bytes()?.as_slice());
// }
-
layers_writer.write_bytes(self.layers.first().unwrap().to_bytes()?.as_slice());
+ layers_writer.write_bytes(self.layer.to_bytes()?.as_slice());
// global_binary_header:| hash:50bits | is_compressed:1bit |
write_fields_meta:1bit | meta_size:12bits |
let meta_size = layers_writer.len() as i64;
let mut header: i64 = min(META_SIZE_MASK, meta_size);
diff --git a/rust/fory-core/src/resolver/context.rs
b/rust/fory-core/src/resolver/context.rs
index b8e031179..4b3f81cc9 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -44,14 +44,17 @@ impl WriteContext {
}
}
+ #[inline(always)]
pub fn empty(&mut self) -> bool {
self.meta_resolver.empty()
}
+ #[inline(always)]
pub fn push_meta(&mut self, fory: &Fory, type_id: std::any::TypeId) ->
usize {
self.meta_resolver.push(type_id, fory)
}
+ #[inline(always)]
pub fn write_meta(&mut self, offset: usize) {
self.writer.set_bytes(
offset,
@@ -107,11 +110,13 @@ impl WriteContext {
}
}
+ #[inline(always)]
pub fn write_meta_string_bytes(&mut self, ms: &MetaString) {
self.meta_string_resolver
.write_meta_string_bytes(&mut self.writer, ms);
}
+ #[inline(always)]
pub fn reset(&mut self) {
self.meta_resolver.reset();
self.ref_writer.reset();
@@ -140,16 +145,19 @@ impl ReadContext {
}
}
+ #[inline(always)]
pub fn init(&mut self, bytes: &[u8], max_dyn_depth: u32) {
self.reader.init(bytes);
self.max_dyn_depth = max_dyn_depth;
self.current_depth = 0;
}
+ #[inline(always)]
pub fn get_meta(&self, type_index: usize) -> &Arc<TypeMeta> {
self.meta_resolver.get(type_index)
}
+ #[inline(always)]
pub fn load_meta(&mut self, offset: usize) -> usize {
self.meta_resolver.load(&mut Reader::new(
&self.reader.slice_after_cursor()[offset..],
@@ -200,6 +208,7 @@ impl ReadContext {
.read_meta_string_bytes(&mut self.reader)
}
+ #[inline(always)]
pub fn inc_depth(&mut self) -> Result<(), crate::error::Error> {
self.current_depth += 1;
if self.current_depth > self.max_dyn_depth {
@@ -215,10 +224,12 @@ impl ReadContext {
Ok(())
}
+ #[inline(always)]
pub fn dec_depth(&mut self) {
self.current_depth = self.current_depth.saturating_sub(1);
}
+ #[inline(always)]
pub fn reset(&mut self) {
self.reader.reset();
self.meta_resolver.reset();
@@ -239,6 +250,7 @@ impl<T> Pool<T> {
}
}
+ #[inline(always)]
pub fn get(&self) -> T {
let item = self
.items
@@ -251,6 +263,7 @@ impl<T> Pool<T> {
}
// put back manually
+ #[inline(always)]
pub fn put(&self, item: T) {
self.items.lock().unwrap().push(item);
}
diff --git a/rust/fory-core/src/resolver/type_resolver.rs
b/rust/fory-core/src/resolver/type_resolver.rs
index ab3c57735..b0f9a5c9d 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -23,7 +23,7 @@ use crate::meta::{
TYPE_NAME_ENCODINGS,
};
use crate::serializer::{ForyDefault, Serializer, StructSerializer};
-use std::sync::{Arc, RwLock};
+use std::sync::Arc;
use std::{any::Any, collections::HashMap};
type WriteFn = fn(&dyn Any, fory: &Fory, &mut WriteContext, is_field: bool);
@@ -182,7 +182,6 @@ pub struct TypeResolver {
type_info_cache: HashMap<std::any::TypeId, TypeInfo>,
// Fast lookup by numeric ID for common types
type_id_index: Vec<u32>,
- sorted_field_names_map: RwLock<HashMap<std::any::TypeId,
Arc<Vec<String>>>>,
}
const NO_TYPE_ID: u32 = 1000000000;
@@ -196,7 +195,6 @@ impl Default for TypeResolver {
type_name_map: HashMap::new(),
type_info_cache: HashMap::new(),
type_id_index: Vec::new(),
- sorted_field_names_map: RwLock::new(HashMap::new()),
};
resolver.register_builtin_types();
resolver
@@ -544,19 +542,6 @@ impl TypeResolver {
.expect("named_ext type must be registered in both peers")
}
- pub fn get_sorted_field_names<T: StructSerializer>(
- &self,
- type_id: std::any::TypeId,
- ) -> Option<Arc<Vec<String>>> {
- let map = self.sorted_field_names_map.read().unwrap();
- map.get(&type_id).cloned()
- }
-
- pub fn set_sorted_field_names<T: StructSerializer>(&self, field_names:
Arc<Vec<String>>) {
- let mut map = self.sorted_field_names_map.write().unwrap();
- map.insert(std::any::TypeId::of::<T>(), field_names);
- }
-
pub fn get_fory_type_id(&self, rust_type_id: std::any::TypeId) ->
Option<u32> {
if let Some(type_info) = self.type_info_cache.get(&rust_type_id) {
Some(type_info.get_type_id())
diff --git a/rust/fory-core/src/serializer/bool.rs
b/rust/fory-core/src/serializer/bool.rs
index c3a71449d..a890816d9 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -24,10 +24,12 @@ use crate::types::TypeId;
use std::mem;
impl Serializer for bool {
+ #[inline(always)]
fn fory_write_data(&self, _fory: &Fory, context: &mut WriteContext,
_is_field: bool) {
context.writer.write_u8(if *self { 1 } else { 0 });
}
+ #[inline(always)]
fn fory_read_data(
_fory: &Fory,
context: &mut ReadContext,
@@ -36,10 +38,12 @@ impl Serializer for bool {
Ok(context.reader.read_u8() == 1)
}
+ #[inline(always)]
fn fory_reserved_space() -> usize {
mem::size_of::<i32>()
}
+ #[inline(always)]
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::BOOL as u32
}
@@ -48,20 +52,24 @@ impl Serializer for bool {
TypeId::BOOL as u32
}
+ #[inline(always)]
fn as_any(&self) -> &dyn std::any::Any {
self
}
+ #[inline(always)]
fn fory_write_type_info(fory: &Fory, context: &mut WriteContext, is_field:
bool) {
write_type_info::<Self>(fory, context, is_field);
}
+ #[inline(always)]
fn fory_read_type_info(fory: &Fory, context: &mut ReadContext, is_field:
bool) {
read_type_info::<Self>(fory, context, is_field);
}
}
impl ForyDefault for bool {
+ #[inline(always)]
fn fory_default() -> Self {
false
}
diff --git a/rust/fory-core/src/serializer/mod.rs
b/rust/fory-core/src/serializer/mod.rs
index 0f3221f7f..c91eb09ab 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -22,7 +22,6 @@ use crate::resolver::context::{ReadContext, WriteContext};
use crate::types::{Mode, RefFlag, TypeId, PRIMITIVE_TYPES};
use anyhow::anyhow;
use std::any::Any;
-use std::sync::Arc;
pub mod any;
mod arc;
@@ -47,6 +46,7 @@ pub mod struct_;
pub mod trait_object;
pub mod weak;
+#[inline(always)]
pub fn write_ref_info_data<T: Serializer + 'static>(
record: &T,
fory: &Fory,
@@ -68,6 +68,7 @@ pub fn write_ref_info_data<T: Serializer + 'static>(
}
}
+#[inline(always)]
pub fn read_ref_info_data<T: Serializer + ForyDefault>(
fory: &Fory,
context: &mut ReadContext,
@@ -105,6 +106,7 @@ pub fn read_ref_info_data<T: Serializer + ForyDefault>(
}
}
+#[inline(always)]
fn write_type_info<T: Serializer>(fory: &Fory, context: &mut WriteContext,
is_field: bool) {
if is_field {
return;
@@ -113,6 +115,7 @@ fn write_type_info<T: Serializer>(fory: &Fory, context:
&mut WriteContext, is_fi
context.writer.write_varuint32(type_id);
}
+#[inline(always)]
fn read_type_info<T: Serializer>(fory: &Fory, context: &mut ReadContext,
is_field: bool) {
if is_field {
return;
@@ -122,6 +125,7 @@ fn read_type_info<T: Serializer>(fory: &Fory, context: &mut
ReadContext, is_fiel
assert_eq!(local_type_id, remote_type_id);
}
+#[inline(always)]
pub fn get_skip_ref_flag<T: Serializer>(fory: &Fory) -> bool {
let elem_type_id = T::fory_get_type_id(fory);
!T::fory_is_option() && PRIMITIVE_TYPES.contains(&elem_type_id)
@@ -325,7 +329,7 @@ pub trait StructSerializer: Serializer + 'static {
struct_::actual_type_id(type_id, register_by_name, mode)
}
- fn fory_get_sorted_field_names(_fory: &Fory) -> Arc<Vec<String>> {
- unimplemented!()
+ fn fory_get_sorted_field_names(_fory: &Fory) -> &'static [&'static str] {
+ &[]
}
}
diff --git a/rust/fory-core/src/serializer/number.rs
b/rust/fory-core/src/serializer/number.rs
index 429afc4de..e3646cb11 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -26,10 +26,12 @@ use crate::types::TypeId;
macro_rules! impl_num_serializer {
($ty:ty, $writer:expr, $reader:expr, $field_type:expr) => {
impl Serializer for $ty {
+ #[inline]
fn fory_write_data(&self, _fory: &Fory, context: &mut
WriteContext, _is_field: bool) {
$writer(&mut context.writer, *self);
}
+ #[inline]
fn fory_read_data(
_fory: &Fory,
context: &mut ReadContext,
@@ -38,10 +40,12 @@ macro_rules! impl_num_serializer {
Ok($reader(&mut context.reader))
}
+ #[inline]
fn fory_reserved_space() -> usize {
std::mem::size_of::<$ty>()
}
+ #[inline]
fn fory_get_type_id(_fory: &Fory) -> u32 {
$field_type as u32
}
@@ -50,19 +54,23 @@ macro_rules! impl_num_serializer {
$field_type as u32
}
+ #[inline]
fn as_any(&self) -> &dyn std::any::Any {
self
}
+ #[inline]
fn fory_write_type_info(fory: &Fory, context: &mut WriteContext,
is_field: bool) {
write_type_info::<Self>(fory, context, is_field);
}
+ #[inline]
fn fory_read_type_info(fory: &Fory, context: &mut ReadContext,
is_field: bool) {
read_type_info::<Self>(fory, context, is_field);
}
}
impl ForyDefault for $ty {
+ #[inline]
fn fory_default() -> Self {
0 as $ty
}
diff --git a/rust/fory-core/src/serializer/option.rs
b/rust/fory-core/src/serializer/option.rs
index 78211b16a..168fdc904 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -22,6 +22,7 @@ use crate::resolver::context::WriteContext;
use crate::serializer::{ForyDefault, Serializer};
impl<T: Serializer + ForyDefault> Serializer for Option<T> {
+ #[inline(always)]
fn fory_read_data(
fory: &Fory,
context: &mut ReadContext,
@@ -30,10 +31,12 @@ impl<T: Serializer + ForyDefault> Serializer for Option<T> {
Ok(Some(T::fory_read_data(fory, context, is_field)?))
}
+ #[inline(always)]
fn fory_read_type_info(fory: &Fory, context: &mut ReadContext, is_field:
bool) {
T::fory_read_type_info(fory, context, is_field);
}
+ #[inline(always)]
fn fory_write_data(&self, fory: &Fory, context: &mut WriteContext,
is_field: bool) {
if let Some(v) = self {
T::fory_write_data(v, fory, context, is_field)
@@ -42,18 +45,22 @@ impl<T: Serializer + ForyDefault> Serializer for Option<T> {
}
}
+ #[inline(always)]
fn fory_write_type_info(fory: &Fory, context: &mut WriteContext, is_field:
bool) {
T::fory_write_type_info(fory, context, is_field);
}
+ #[inline(always)]
fn fory_reserved_space() -> usize {
std::mem::size_of::<T>()
}
+ #[inline(always)]
fn fory_get_type_id(fory: &Fory) -> u32 {
T::fory_get_type_id(fory)
}
+ #[inline(always)]
fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
match self {
Some(val) => val.fory_type_id_dyn(fory),
@@ -61,20 +68,24 @@ impl<T: Serializer + ForyDefault> Serializer for Option<T> {
}
}
+ #[inline(always)]
fn fory_is_option() -> bool {
true
}
+ #[inline(always)]
fn fory_is_none(&self) -> bool {
self.is_none()
}
+ #[inline(always)]
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl<T: ForyDefault> ForyDefault for Option<T> {
+ #[inline(always)]
fn fory_default() -> Self {
None
}
diff --git a/rust/fory-core/src/serializer/string.rs
b/rust/fory-core/src/serializer/string.rs
index 9fb68d69b..e6a7a0b69 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -31,6 +31,7 @@ enum StrEncoding {
}
impl Serializer for String {
+ #[inline]
fn fory_write_data(&self, fory: &Fory, context: &mut WriteContext,
_is_field: bool) {
let mut len = get_latin1_length(self);
if len >= 0 {
@@ -51,6 +52,7 @@ impl Serializer for String {
}
}
+ #[inline]
fn fory_read_data(
_fory: &Fory,
context: &mut ReadContext,
@@ -75,10 +77,12 @@ impl Serializer for String {
Ok(s)
}
+ #[inline]
fn fory_reserved_space() -> usize {
mem::size_of::<i32>()
}
+ #[inline(always)]
fn fory_get_type_id(_fory: &Fory) -> u32 {
TypeId::STRING as u32
}
@@ -87,20 +91,24 @@ impl Serializer for String {
TypeId::STRING as u32
}
+ #[inline(always)]
fn as_any(&self) -> &dyn std::any::Any {
self
}
+ #[inline(always)]
fn fory_write_type_info(fory: &Fory, context: &mut WriteContext, is_field:
bool) {
write_type_info::<Self>(fory, context, is_field);
}
+ #[inline(always)]
fn fory_read_type_info(fory: &Fory, context: &mut ReadContext, is_field:
bool) {
read_type_info::<Self>(fory, context, is_field);
}
}
impl ForyDefault for String {
+ #[inline(always)]
fn fory_default() -> Self {
String::new()
}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index d89d25745..9da9cd705 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -83,7 +83,6 @@ pub enum TypeId {
ARROW_RECORD_BATCH = 38,
ARROW_TABLE = 39,
UNKNOWN = 63,
- ForyAny = 256,
// only used at receiver peer
ForyNullable = 265,
}
@@ -134,12 +133,6 @@ pub static PRIMITIVE_TYPES: [u32; 7] = [
TypeId::FLOAT64 as u32,
];
-pub static FINAL_TYPES: [u32; 3] = [
- TypeId::STRING as u32,
- TypeId::LOCAL_DATE as u32,
- TypeId::TIMESTAMP as u32,
-];
-
pub static PRIMITIVE_ARRAY_TYPES: [u32; 8] = [
TypeId::BOOL_ARRAY as u32,
TypeId::BINARY as u32,
@@ -179,6 +172,23 @@ pub static PRIMITIVE_ARRAY_TYPE_MAP: &[(&str, u32, &str)]
= &[
("f64", TypeId::FLOAT64_ARRAY as u32, "Vec<f64>"),
];
+pub fn is_internal_type(type_id: u32) -> bool {
+ if type_id == 0 || type_id >= TypeId::UNKNOWN as u32 {
+ return false;
+ }
+ let excluded = [
+ TypeId::ENUM as u32,
+ TypeId::NAMED_ENUM as u32,
+ TypeId::STRUCT as u32,
+ TypeId::COMPATIBLE_STRUCT as u32,
+ TypeId::NAMED_STRUCT as u32,
+ TypeId::NAMED_COMPATIBLE_STRUCT as u32,
+ TypeId::EXT as u32,
+ TypeId::NAMED_EXT as u32,
+ ];
+ !excluded.contains(&type_id)
+}
+
pub fn compute_field_hash(hash: u32, id: i16) -> u32 {
let mut new_hash: u64 = (hash as u64) * 31 + (id as u64);
while new_hash >= MAX_UNT32 {
diff --git a/rust/fory-derive/src/object/misc.rs
b/rust/fory-derive/src/object/misc.rs
index 80fedeb20..4a2d1374a 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -65,18 +65,9 @@ pub fn gen_actual_type_id() -> TokenStream {
}
pub fn gen_get_sorted_field_names(fields: &[&Field]) -> TokenStream {
- let create_sorted_field_names = get_sort_fields_ts(fields);
+ let static_field_names = get_sort_fields_ts(fields);
quote! {
- let sorted_field_names = match
fory.get_type_resolver().get_sorted_field_names::<Self>(std::any::TypeId::of::<Self>())
{
- Some(result) => result,
- None => {
- #create_sorted_field_names
- let arc_sorted_field_names =
std::sync::Arc::new(sorted_field_names);
-
fory.get_type_resolver().set_sorted_field_names::<Self>(arc_sorted_field_names.clone());
- arc_sorted_field_names
- }
- };
- sorted_field_names
+ #static_field_names
}
}
diff --git a/rust/fory-derive/src/object/mod.rs
b/rust/fory-derive/src/object/mod.rs
index 6fa6bb026..5e25ce09f 100644
--- a/rust/fory-derive/src/object/mod.rs
+++ b/rust/fory-derive/src/object/mod.rs
@@ -19,7 +19,7 @@ mod derive_enum;
mod misc;
mod read;
mod serializer;
-mod util;
+pub(crate) mod util;
mod write;
pub use serializer::derive_serializer;
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index c87e1bfda..c3ca1c5d7 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -21,7 +21,7 @@ use syn::{Field, Type};
use super::util::{
classify_trait_object_field, create_wrapper_types_arc,
create_wrapper_types_rc,
- generic_tree_to_tokens, parse_generic_tree, NullableTypeNode, StructField,
+ generic_tree_to_tokens, parse_generic_tree, skip_ref_flag,
NullableTypeNode, StructField,
};
fn create_private_field_name(field: &Field) -> Ident {
@@ -83,33 +83,30 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
.collect()
}
-fn gen_read_match_arm(field: &Field, private_ident: &Ident) -> TokenStream {
+fn gen_read_field(field: &Field, private_ident: &Ident) -> TokenStream {
let ty = &field.ty;
- let name_str = field.ident.as_ref().unwrap().to_string();
-
match classify_trait_object_field(ty) {
StructField::BoxDyn(trait_name) => {
let from_any_fn = format_ident!("from_any_internal_{}",
trait_name);
let helper_mod = format_ident!("__fory_trait_helpers_{}",
trait_name);
quote! {
- #name_str => {
- let ref_flag = context.reader.read_i8();
- if ref_flag != fory_core::types::RefFlag::NotNullValue as
i8 {
- panic!("Expected NotNullValue for trait object field");
- }
+ let ref_flag = context.reader.read_i8();
+ if ref_flag != fory_core::types::RefFlag::NotNullValue as i8 {
+ panic!("Expected NotNullValue for trait object field");
+ }
- let fory_type_id = context.reader.read_varuint32();
+ let fory_type_id = context.reader.read_varuint32();
- let harness = fory.get_type_resolver()
- .get_harness(fory_type_id)
- .expect("Type not registered for trait object field");
+ let harness = fory.get_type_resolver()
+ .get_harness(fory_type_id)
+ .expect("Type not registered for trait object field");
- let deserializer_fn = harness.get_read_fn();
- let any_box = deserializer_fn(fory, context, true, false)?;
+ let deserializer_fn = harness.get_read_fn();
+ let any_box = deserializer_fn(fory, context, true, false)?;
+
+ let base_type_id = fory_type_id >> 8;
+ let #private_ident = #helper_mod::#from_any_fn(any_box,
base_type_id)?;
- let base_type_id = fory_type_id >> 8;
- #private_ident = #helper_mod::#from_any_fn(any_box,
base_type_id)?;
- }
}
}
StructField::RcDyn(trait_name) => {
@@ -117,10 +114,8 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper = <#wrapper_ty as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
- #private_ident = std::rc::Rc::<dyn
#trait_ident>::from(wrapper);
- }
+ let wrapper = <#wrapper_ty as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
+ let #private_ident = std::rc::Rc::<dyn
#trait_ident>::from(wrapper);
}
}
StructField::ArcDyn(trait_name) => {
@@ -128,10 +123,8 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper = <#wrapper_ty as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
- #private_ident = std::sync::Arc::<dyn
#trait_ident>::from(wrapper);
- }
+ let wrapper = <#wrapper_ty as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
+ let #private_ident = std::sync::Arc::<dyn
#trait_ident>::from(wrapper);
}
}
StructField::VecRc(trait_name) => {
@@ -139,12 +132,10 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper_vec = <Vec<#wrapper_ty> as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
- #private_ident = Some(wrapper_vec.into_iter()
- .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
- .collect());
- }
+ let wrapper_vec = <Vec<#wrapper_ty> as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
+ let #private_ident = wrapper_vec.into_iter()
+ .map(|w| std::rc::Rc::<dyn #trait_ident>::from(w))
+ .collect();
}
}
StructField::VecArc(trait_name) => {
@@ -152,12 +143,10 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper_vec = <Vec<#wrapper_ty> as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
- #private_ident = Some(wrapper_vec.into_iter()
- .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
- .collect());
- }
+ let wrapper_vec = <Vec<#wrapper_ty> as
fory_core::serializer::Serializer>::fory_read(fory, context, true)?;
+ let #private_ident = wrapper_vec.into_iter()
+ .map(|w| std::sync::Arc::<dyn #trait_ident>::from(w))
+ .collect();
}
}
StructField::HashMapRc(key_ty, trait_name) => {
@@ -165,12 +154,10 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper_map = <std::collections::HashMap<#key_ty,
#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(fory, context,
true)?;
- #private_ident = Some(wrapper_map.into_iter()
- .map(|(k, v)| (k, std::rc::Rc::<dyn
#trait_ident>::from(v)))
- .collect());
- }
+ let wrapper_map = <std::collections::HashMap<#key_ty,
#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(fory, context,
true)?;
+ let #private_ident = wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::rc::Rc::<dyn
#trait_ident>::from(v)))
+ .collect();
}
}
StructField::HashMapArc(key_ty, trait_name) => {
@@ -178,27 +165,21 @@ fn gen_read_match_arm(field: &Field, private_ident:
&Ident) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
- let wrapper_map = <std::collections::HashMap<#key_ty,
#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(fory, context,
true)?;
- #private_ident = Some(wrapper_map.into_iter()
- .map(|(k, v)| (k, std::sync::Arc::<dyn
#trait_ident>::from(v)))
- .collect());
- }
+ let wrapper_map = <std::collections::HashMap<#key_ty,
#wrapper_ty> as fory_core::serializer::Serializer>::fory_read(fory, context,
true)?;
+ let #private_ident = wrapper_map.into_iter()
+ .map(|(k, v)| (k, std::sync::Arc::<dyn
#trait_ident>::from(v)))
+ .collect();
}
}
StructField::Forward => {
quote! {
- #name_str => {
- #private_ident =
Some(fory_core::serializer::Serializer::fory_read(fory, context, true)?);
- }
+ let #private_ident =
fory_core::serializer::Serializer::fory_read(fory, context, true)?;
}
}
_ => {
+ let skip_ref_flag = skip_ref_flag(ty);
quote! {
- #name_str => {
- let skip_ref_flag =
fory_core::serializer::get_skip_ref_flag::<#ty>(fory);
- #private_ident =
Some(fory_core::serializer::read_ref_info_data::<#ty>(fory, context, true,
skip_ref_flag, false)?);
- }
+ let #private_ident =
fory_core::serializer::read_ref_info_data::<#ty>(fory, context, true,
#skip_ref_flag, false)?;
}
}
}
@@ -211,94 +192,32 @@ pub fn gen_read_type_info() -> TokenStream {
}
fn get_fields_loop_ts(fields: &[&Field]) -> TokenStream {
- let match_ts: Vec<_> = fields
+ let read_fields_ts: Vec<_> = fields
.iter()
.map(|field| {
let private_ident = create_private_field_name(field);
- gen_read_match_arm(field, &private_ident)
+ gen_read_field(field, &private_ident)
})
.collect();
- #[cfg(not(feature = "fields-loop-unroll"))]
- let loop_ts = quote! {
- for field_name in field_names {
- match field_name.as_str() {
- #(#match_ts),*
- , _ => unreachable!()
- }
- }
- };
- #[cfg(feature = "fields-loop-unroll")]
- let loop_ts = {
- let loop_item_ts = fields.iter().enumerate().map(|(i, _field)| {
- let idx = syn::Index::from(i);
- quote! {
- let field_name = field_names.get(#idx).unwrap();
- match field_name.as_str() {
- #(#match_ts),*
- , _ => { unreachable!() }
- }
- }
- });
- quote! {
- #(#loop_item_ts)*
- }
- };
- loop_ts
+ quote! {
+ #(#read_fields_ts)*
+ }
}
pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
let sorted_read = if fields.is_empty() {
quote! {}
} else {
- let declare_var_ts =
- fields
- .iter()
- .map(|field| {
- let private_ident = create_private_field_name(field);
- let ty = &field.ty;
- match classify_trait_object_field(ty) {
- StructField::BoxDyn(_)
- | StructField::RcDyn(_)
- | StructField::ArcDyn(_) => {
- quote! {
- let mut #private_ident: #ty = <#ty as
fory_core::serializer::ForyDefault>::fory_default();
- }
- }
- _ => {
- quote! {
- let mut #private_ident: Option<#ty> = None;
- }
- }
- }
- });
let loop_ts = get_fields_loop_ts(fields);
quote! {
- #(#declare_var_ts)*
- let field_names = <Self as
fory_core::serializer::StructSerializer>::fory_get_sorted_field_names(fory);
- let field_names = field_names.as_ref();
#loop_ts
}
};
let field_idents = fields.iter().map(|field| {
let private_ident = create_private_field_name(field);
let original_ident = &field.ident;
- let ty = &field.ty;
- match classify_trait_object_field(ty) {
- StructField::BoxDyn(_) | StructField::RcDyn(_) |
StructField::ArcDyn(_) => {
- quote! {
- #original_ident: #private_ident
- }
- }
- StructField::ContainsTraitObject => {
- quote! {
- #original_ident: #private_ident.unwrap()
- }
- }
- _ => {
- quote! {
- #original_ident: #private_ident.unwrap_or_default()
- }
- }
+ quote! {
+ #original_ident: #private_ident
}
});
quote! {
@@ -501,7 +420,6 @@ pub fn gen_read_compatible(fields: &[&Field]) ->
TokenStream {
let declare_ts: Vec<TokenStream> = declare_var(fields);
let assign_ts: Vec<TokenStream> = assign_value(fields);
- let consistent_fields_loop_ts = get_fields_loop_ts(fields);
quote! {
let remote_type_id = context.reader.read_varuint32();
let meta_index = context.reader.read_varuint32();
@@ -517,9 +435,7 @@ pub fn gen_read_compatible(fields: &[&Field]) ->
TokenStream {
let local_type_hash =
i64::from_le_bytes(high_bytes.try_into().unwrap());
if meta.get_hash() == local_type_hash {
// fast path
- let field_names = <Self as
fory_core::serializer::StructSerializer>::fory_get_sorted_field_names(fory);
- let field_names = field_names.as_ref();
- #consistent_fields_loop_ts
+ <Self as fory_core::serializer::Serializer>::fory_read_data(fory,
context, false)
} else {
for _field in fields.iter() {
#(#pattern_items else)* {
@@ -529,10 +445,10 @@ pub fn gen_read_compatible(fields: &[&Field]) ->
TokenStream {
fory_core::serializer::skip::skip_field_value(fory,
context, &nullable_field_type, read_ref_flag).unwrap();
}
}
+ Ok(Self {
+ #(#assign_ts),*
+ })
}
- Ok(Self {
- #(#assign_ts),*
- })
}
}
diff --git a/rust/fory-derive/src/object/serializer.rs
b/rust/fory-derive/src/object/serializer.rs
index 20f0421c4..53b07f20d 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -135,7 +135,7 @@ pub fn derive_serializer(ast: &syn::DeriveInput) ->
TokenStream {
#actual_type_id_ts
}
- fn fory_get_sorted_field_names(fory: &fory_core::fory::Fory) ->
std::sync::Arc<Vec<String>> {
+ fn fory_get_sorted_field_names(_fory: &fory_core::fory::Fory) ->
&'static [&'static str] {
#get_sorted_field_names_ts
}
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index 8626bf462..6841dfa14 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -250,7 +250,7 @@ macro_rules! basic_type_deserialize {
};
}
-pub fn try_primitive_vec_type(node: &TypeNode) -> Option<TokenStream> {
+pub(super) fn try_primitive_vec_type(node: &TypeNode) -> Option<TokenStream> {
if node.name != "Vec" {
return None;
}
@@ -263,7 +263,7 @@ pub fn try_primitive_vec_type(node: &TypeNode) ->
Option<TokenStream> {
None
}
-pub fn try_vec_of_option_primitive(node: &TypeNode) -> Option<TokenStream> {
+pub(super) fn try_vec_of_option_primitive(node: &TypeNode) ->
Option<TokenStream> {
if node.name != "Vec" {
return None;
}
@@ -283,7 +283,7 @@ pub fn try_vec_of_option_primitive(node: &TypeNode) ->
Option<TokenStream> {
None
}
-pub fn try_primitive_vec_type_name(node: &NullableTypeNode) -> Option<String> {
+pub(super) fn try_primitive_vec_type_name(node: &NullableTypeNode) ->
Option<String> {
if node.name != "Vec" {
return None;
}
@@ -782,367 +782,223 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode)
-> TokenStream {
}
type FieldGroup = Vec<(String, String, u32)>;
-type FieldGroups = (FieldGroup, FieldGroup, FieldGroup, FieldGroup);
-pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
- fn group_fields(fields: &[&Field]) -> FieldGroups {
- const PRIMITIVE_TYPE_NAMES: [&str; 7] = ["bool", "i8", "i16", "i32",
"i64", "f32", "f64"];
- const FINAL_TYPE_NAMES: [&str; 3] = ["String", "NaiveDate",
"NaiveDateTime"];
- const PRIMITIVE_ARRAY_NAMES: [&str; 7] = [
- "Vec<bool>",
- "Vec<i8>",
- "Vec<i16>",
- "Vec<i32>",
- "Vec<i64>",
- "Vec<f32>",
- "Vec<f64>",
- ];
-
- fn extract_option_inner(s: &str) -> Option<&str> {
- s.strip_prefix("Option<")?.strip_suffix(">")
- }
-
- macro_rules! match_ty {
- ($ty:expr, $(($name:expr, $ret:expr)),+ $(,)?) => {
- $(
- if $ty == $name {
- $ret as u32
- } else
- )+
- {
- unreachable!("Unknown type: {}", $ty);
- }
- };
- }
-
- fn get_primitive_type_id(ty: &str) -> u32 {
- match_ty!(
- ty,
- ("bool", TypeId::BOOL),
- ("i8", TypeId::INT8),
- ("i16", TypeId::INT16),
- ("i32", TypeId::INT32),
- ("i64", TypeId::INT64),
- ("f32", TypeId::FLOAT32),
- ("f64", TypeId::FLOAT64),
- )
- }
-
- let mut primitive_fields = Vec::new();
- let mut nullable_primitive_fields = Vec::new();
- let mut final_fields = Vec::new();
- let mut collection_fields = Vec::new();
- let mut map_fields = Vec::new();
- let mut struct_or_enum_fields = Vec::new();
-
- // First handle Forward fields separately to avoid borrow checker
issues
- for field in fields {
- if is_forward_field(&field.ty) {
- let ident = field.ident.as_ref().unwrap().to_string();
- collection_fields.push((ident, "Forward".to_string(),
TypeId::LIST as u32));
- }
- }
+type FieldGroups = (
+ FieldGroup,
+ FieldGroup,
+ FieldGroup,
+ FieldGroup,
+ FieldGroup,
+ FieldGroup,
+ FieldGroup,
+);
+
+const PRIMITIVE_TYPE_NAMES: [&str; 7] = ["bool", "i8", "i16", "i32", "i64",
"f32", "f64"];
+
+fn get_primitive_type_id(ty: &str) -> u32 {
+ match ty {
+ "bool" => TypeId::BOOL as u32,
+ "i8" => TypeId::INT8 as u32,
+ "i16" => TypeId::INT16 as u32,
+ "i32" => TypeId::INT32 as u32,
+ "i64" => TypeId::INT64 as u32,
+ "f32" => TypeId::FLOAT32 as u32,
+ "f64" => TypeId::FLOAT64 as u32,
+ _ => unreachable!("Unknown primitive type: {}", ty),
+ }
+}
- let mut group_field = |ident: String, ty: &str| {
- if PRIMITIVE_TYPE_NAMES.contains(&ty) {
- let type_id = get_primitive_type_id(ty);
- primitive_fields.push((ident, ty.to_string(), type_id));
- } else if FINAL_TYPE_NAMES.contains(&ty) ||
PRIMITIVE_ARRAY_NAMES.contains(&ty) {
- let type_id = match_ty!(
- ty,
- ("String", TypeId::STRING),
- ("NaiveDate", TypeId::LOCAL_DATE),
- ("NaiveDateTime", TypeId::TIMESTAMP),
- ("Vec<u8>", TypeId::BINARY),
- ("Vec<bool>", TypeId::BOOL_ARRAY),
- ("Vec<i8>", TypeId::INT8_ARRAY),
- ("Vec<i16>", TypeId::INT16_ARRAY),
- ("Vec<i32>", TypeId::INT32_ARRAY),
- ("Vec<i64>", TypeId::INT64_ARRAY),
- ("Vec<f32>", TypeId::FLOAT32_ARRAY),
- ("Vec<f64>", TypeId::FLOAT64_ARRAY),
- );
- final_fields.push((ident, ty.to_string(), type_id));
- } else if ty.starts_with("Vec<")
- || ty.starts_with("VecDeque<")
- || ty.starts_with("LinkedList<")
- || ty.starts_with("BinaryHeap<")
- {
- collection_fields.push((ident, ty.to_string(), TypeId::LIST as
u32));
- } else if ty.starts_with("HashSet<") ||
ty.starts_with("BTreeSet<") {
- collection_fields.push((ident, ty.to_string(), TypeId::SET as
u32));
- } else if ty.starts_with("HashMap<") ||
ty.starts_with("BTreeMap<") {
- map_fields.push((ident, ty.to_string(), TypeId::MAP as u32));
- } else {
- struct_or_enum_fields.push((ident, ty.to_string(), 0));
- }
- };
+fn group_fields_by_type(fields: &[&Field]) -> FieldGroups {
+ fn extract_option_inner(s: &str) -> Option<&str> {
+ s.strip_prefix("Option<")?.strip_suffix(">")
+ }
- for field in fields {
+ let mut primitive_fields = Vec::new();
+ let mut nullable_primitive_fields = Vec::new();
+ let mut internal_type_fields = Vec::new();
+ let mut list_fields = Vec::new();
+ let mut set_fields = Vec::new();
+ let mut map_fields = Vec::new();
+ let mut other_fields = Vec::new();
+
+ // First handle Forward fields separately to avoid borrow checker issues
+ for field in fields {
+ if is_forward_field(&field.ty) {
let ident = field.ident.as_ref().unwrap().to_string();
-
- // Skip if already handled as Forward field
- if is_forward_field(&field.ty) {
- continue;
- }
-
- let ty: String = field
- .ty
- .to_token_stream()
- .to_string()
- .chars()
- .filter(|c| !c.is_whitespace())
- .collect::<String>();
- // handle Option<Primitive> specially
- if let Some(inner) = extract_option_inner(&ty) {
- if PRIMITIVE_TYPE_NAMES.contains(&inner) {
- let type_id = get_primitive_type_id(inner);
- nullable_primitive_fields.push((ident, ty.to_string(),
type_id));
- } else {
- // continue to handle Option<not Primitive>
- // already avoid Option<Option<T>> at compile-time
- group_field(ident, inner);
- }
- } else {
- group_field(ident, &ty);
- }
- }
-
- for field in fields {
- if is_box_dyn_trait(&field.ty).is_some() {
- let ident = field.ident.as_ref().unwrap().to_string();
- if let Some(pos) = struct_or_enum_fields.iter().position(|x|
x.0 == ident) {
- struct_or_enum_fields[pos].2 = TypeId::UNKNOWN as u32;
- }
- }
+ other_fields.push((ident, "Forward".to_string(), TypeId::UNKNOWN
as u32));
}
+ }
- fn sorter(a: &(String, String, u32), b: &(String, String, u32)) ->
std::cmp::Ordering {
- a.2.cmp(&b.2).then_with(|| a.0.cmp(&b.0))
- }
- fn get_primitive_type_size(type_id_num: u32) -> i32 {
- let type_id = TypeId::try_from(type_id_num as i16).unwrap();
- match type_id {
- TypeId::BOOL => 1,
- TypeId::INT8 => 1,
- TypeId::INT16 => 2,
- TypeId::INT32 => 4,
- TypeId::VAR_INT32 => 4,
- TypeId::INT64 => 8,
- TypeId::VAR_INT64 => 8,
- TypeId::FLOAT16 => 2,
- TypeId::FLOAT32 => 4,
- TypeId::FLOAT64 => 8,
- _ => unreachable!(),
- }
+ fn get_other_internal_type_id(ty: &str) -> u32 {
+ match ty {
+ "String" => TypeId::STRING as u32,
+ "NaiveDate" => TypeId::LOCAL_DATE as u32,
+ "NaiveDateTime" => TypeId::TIMESTAMP as u32,
+ "Duration" => TypeId::DURATION as u32,
+ "Decimal" => TypeId::DECIMAL as u32,
+ "Vec<u8>" | "bytes" => TypeId::BINARY as u32,
+ "Vec<bool>" => TypeId::BOOL_ARRAY as u32,
+ "Vec<i8>" => TypeId::INT8_ARRAY as u32,
+ "Vec<i16>" => TypeId::INT16_ARRAY as u32,
+ "Vec<i32>" => TypeId::INT32_ARRAY as u32,
+ "Vec<i64>" => TypeId::INT64_ARRAY as u32,
+ "Vec<f16>" => TypeId::FLOAT16_ARRAY as u32,
+ "Vec<f32>" => TypeId::FLOAT32_ARRAY as u32,
+ "Vec<f64>" => TypeId::FLOAT64_ARRAY as u32,
+ _ => 0,
}
+ }
- fn is_compress(type_id: u32) -> bool {
- [
- TypeId::INT32 as u32,
- TypeId::INT64 as u32,
- TypeId::VAR_INT32 as u32,
- TypeId::VAR_INT64 as u32,
- ]
- .contains(&type_id)
+ let mut group_field = |ident: String, ty: &str| {
+ if PRIMITIVE_TYPE_NAMES.contains(&ty) {
+ primitive_fields.push((ident, ty.to_string(),
get_primitive_type_id(ty)));
+ } else if get_other_internal_type_id(ty) > 0 {
+ let internal_type_id = get_other_internal_type_id(ty);
+ internal_type_fields.push((ident, ty.to_string(),
internal_type_id));
+ } else if ty.starts_with("Vec<")
+ || ty.starts_with("VecDeque<")
+ || ty.starts_with("LinkedList<")
+ || ty.starts_with("BinaryHeap<")
+ {
+ list_fields.push((ident, ty.to_string(), TypeId::LIST as u32));
+ } else if ty.starts_with("HashSet<") || ty.starts_with("BTreeSet<") {
+ set_fields.push((ident, ty.to_string(), TypeId::SET as u32));
+ } else if ty.starts_with("HashMap<") || ty.starts_with("BTreeMap<") {
+ map_fields.push((ident, ty.to_string(), TypeId::MAP as u32));
+ } else {
+ other_fields.push((ident, ty.to_string(), TypeId::UNKNOWN as u32));
}
+ };
- fn numeric_sorter(
- a: &(String, String, u32),
- b: &(String, String, u32),
- ) -> std::cmp::Ordering {
- let compress_a = is_compress(a.2);
- let compress_b = is_compress(b.2);
- let size_a = get_primitive_type_size(a.2);
- let size_b = get_primitive_type_size(b.2);
- compress_a
- .cmp(&compress_b)
- .then_with(|| size_b.cmp(&size_a))
- .then_with(|| a.0.cmp(&b.0))
+ for field in fields {
+ let ident = field.ident.as_ref().unwrap().to_string();
+
+ // Skip if already handled as Forward field
+ if is_forward_field(&field.ty) {
+ continue;
+ }
+
+ let ty: String = field
+ .ty
+ .to_token_stream()
+ .to_string()
+ .chars()
+ .filter(|c| !c.is_whitespace())
+ .collect::<String>();
+ // handle Option<Primitive> specially
+ if let Some(inner) = extract_option_inner(&ty) {
+ if PRIMITIVE_TYPE_NAMES.contains(&inner) {
+ let type_id = get_primitive_type_id(inner);
+ nullable_primitive_fields.push((ident, ty.to_string(),
type_id));
+ } else {
+ group_field(ident, inner);
+ }
+ } else {
+ group_field(ident, &ty);
}
-
- primitive_fields.sort_by(numeric_sorter);
- nullable_primitive_fields.sort_by(numeric_sorter);
- primitive_fields.extend(nullable_primitive_fields);
- collection_fields.sort_by(sorter);
- map_fields.sort_by(sorter);
- let container_fields = {
- let mut container_fields = collection_fields;
- container_fields.extend(map_fields);
- container_fields
- };
- (
- primitive_fields,
- final_fields,
- container_fields,
- struct_or_enum_fields,
- )
}
- fn gen_vec_token_stream(fields: &[(String, String, u32)]) -> TokenStream {
- let names = fields.iter().map(|(name, _, _)| {
- quote! { #name.to_string() }
- });
- quote! {
- vec![#(#names),*]
+ fn get_primitive_type_size(type_id_num: u32) -> i32 {
+ let type_id = TypeId::try_from(type_id_num as i16).unwrap();
+ match type_id {
+ TypeId::BOOL => 1,
+ TypeId::INT8 => 1,
+ TypeId::INT16 => 2,
+ TypeId::INT32 => 4,
+ TypeId::VAR_INT32 => 4,
+ TypeId::INT64 => 8,
+ TypeId::VAR_INT64 => 8,
+ TypeId::FLOAT16 => 2,
+ TypeId::FLOAT32 => 4,
+ TypeId::FLOAT64 => 8,
+ _ => unreachable!(),
}
}
- fn gen_vec_tuple_token_stream(fields: &[(String, String, u32)]) ->
TokenStream {
- let names = fields.iter().map(|(name, _, type_id)| {
- quote! { (#type_id, #name.to_string()) }
- });
- quote! {
- vec![#(#names),*]
- }
+ fn is_compress(type_id: u32) -> bool {
+ [
+ TypeId::INT32 as u32,
+ TypeId::INT64 as u32,
+ TypeId::VAR_INT32 as u32,
+ TypeId::VAR_INT64 as u32,
+ ]
+ .contains(&type_id)
}
- let (all_primitive_fields, final_fields, container_fields,
struct_or_enum_fields) =
- group_fields(fields);
+ fn numeric_sorter(a: &(String, String, u32), b: &(String, String, u32)) ->
std::cmp::Ordering {
+ let compress_a = is_compress(a.2);
+ let compress_b = is_compress(b.2);
+ let size_a = get_primitive_type_size(a.2);
+ let size_b = get_primitive_type_size(b.2);
+ compress_a
+ .cmp(&compress_b)
+ .then_with(|| size_b.cmp(&size_a))
+ .then_with(|| a.2.cmp(&b.2))
+ .then_with(|| a.0.cmp(&b.0))
+ }
- let all_primitive_field_names_declare_extend_ts = {
- if all_primitive_fields.is_empty() {
- (quote! {}, quote! {})
- } else {
- let all_primitive_field_names_ts =
gen_vec_token_stream(&all_primitive_fields);
- (
- quote! {
- let all_primitive_field_names: Vec<String> =
#all_primitive_field_names_ts;
- },
- quote! {
- sorted_field_names.extend(all_primitive_field_names);
- },
- )
- }
- };
- let container_field_names_declare_extend_ts = {
- if container_fields.is_empty() {
- (quote! {}, quote! {})
- } else {
- let container_field_names_ts =
gen_vec_token_stream(&container_fields);
- (
- quote! {
- let container_field_names: Vec<String> =
#container_field_names_ts;
- },
- quote! {
- sorted_field_names.extend(container_field_names);
- },
- )
- }
- };
- let sorter_ts = quote! {
- |a: &(u32, String), b: &(u32, String)| a.0.cmp(&b.0).then_with(||
a.1.cmp(&b.1))
- };
- let final_fields_declare_extend_ts = {
- if final_fields.is_empty() && struct_or_enum_fields.is_empty() {
- (quote! {}, quote! {})
- } else {
- let final_fields_ts = gen_vec_tuple_token_stream(&final_fields);
- (
- quote! {
- let mut final_fields: Vec<(u32, String)> =
#final_fields_ts;
- },
- quote! {
- final_fields.sort_by(#sorter_ts);
- for (_, name) in final_fields.drain(..) {
sorted_field_names.push(name); }
- },
- )
- }
- };
- let other_fields_declare_extend_ts = {
- if struct_or_enum_fields.is_empty() {
- (quote! {}, quote! {})
- } else {
- (
- quote! {
- let mut other_fields: Vec<(u32, String)> = vec![];
- },
- quote! {
- other_fields.sort_by(#sorter_ts);
- for (_, name) in other_fields.drain(..) {
sorted_field_names.push(name); }
- },
- )
- }
- };
- let trait_object_fields_ts = {
- let trait_obj_fields: Vec<_> = struct_or_enum_fields
- .iter()
- .filter(|(_, _, type_id)| *type_id ==
fory_core::types::TypeId::UNKNOWN as u32)
- .collect();
-
- if trait_obj_fields.is_empty() {
- quote! {}
- } else {
- let names = trait_obj_fields.iter().map(|(name, _, type_id)| {
- quote! {
- final_fields.push((#type_id, #name.to_string()));
- }
- });
- quote! {
- #(#names)*
- }
- }
- };
+ fn type_id_then_name_sorter(
+ a: &(String, String, u32),
+ b: &(String, String, u32),
+ ) -> std::cmp::Ordering {
+ a.2.cmp(&b.2).then_with(|| a.0.cmp(&b.0))
+ }
- let group_sort_enum_other_fields = {
- if struct_or_enum_fields.is_empty() {
- quote! {}
- } else {
- let ts = struct_or_enum_fields
- .iter()
- .filter(|(_, _, type_id)| *type_id !=
fory_core::types::TypeId::UNKNOWN as u32)
- .map(|(name, ty, _)| {
- let ty_type: Type = syn::parse_str(ty).unwrap();
- quote! {
- let field_type_id = <#ty_type as
fory_core::serializer::Serializer>::fory_get_type_id(fory);
- let internal_id = field_type_id & 0xff;
- if internal_id ==
fory_core::types::TypeId::COMPATIBLE_STRUCT as u32
- || internal_id ==
fory_core::types::TypeId::NAMED_COMPATIBLE_STRUCT as u32
- || internal_id == fory_core::types::TypeId::STRUCT
as u32
- || internal_id ==
fory_core::types::TypeId::NAMED_STRUCT as u32
- || internal_id == fory_core::types::TypeId::EXT as
u32
- || internal_id ==
fory_core::types::TypeId::NAMED_EXT as u32
- {
- other_fields.push((field_type_id,
#name.to_string()));
- } else if internal_id ==
fory_core::types::TypeId::ENUM as u32 || internal_id ==
fory_core::types::TypeId::NAMED_ENUM as u32 {
- final_fields.push((field_type_id,
#name.to_string()));
- } else {
- unimplemented!("unknown internal_id when
group_sort_enum_other_fields");
- }
- }
- })
- .collect::<Vec<_>>();
- quote! {
- {
- #(#ts)*
- }
- }
- }
- };
+ fn name_sorter(a: &(String, String, u32), b: &(String, String, u32)) ->
std::cmp::Ordering {
+ a.0.cmp(&b.0)
+ }
- let (all_primitive_declare, all_primitive_extend) =
all_primitive_field_names_declare_extend_ts;
- let (container_declare, container_extend) =
container_field_names_declare_extend_ts;
- let (final_declare, final_extend) = final_fields_declare_extend_ts;
- let (other_declare, other_extend) = other_fields_declare_extend_ts;
+ primitive_fields.sort_by(numeric_sorter);
+ nullable_primitive_fields.sort_by(numeric_sorter);
+ internal_type_fields.sort_by(type_id_then_name_sorter);
+ list_fields.sort_by(name_sorter);
+ set_fields.sort_by(name_sorter);
+ map_fields.sort_by(name_sorter);
+ other_fields.sort_by(name_sorter);
+
+ (
+ primitive_fields,
+ nullable_primitive_fields,
+ internal_type_fields,
+ list_fields,
+ set_fields,
+ map_fields,
+ other_fields,
+ )
+}
- let fields_len = fields.len();
+pub(crate) fn get_sorted_field_names(fields: &[&Field]) -> Vec<String> {
+ let (
+ primitive_fields,
+ nullable_primitive_fields,
+ internal_type_fields,
+ list_fields,
+ set_fields,
+ map_fields,
+ other_fields,
+ ) = group_fields_by_type(fields);
+
+ let mut all_fields = primitive_fields;
+ all_fields.extend(nullable_primitive_fields);
+ all_fields.extend(internal_type_fields);
+ all_fields.extend(list_fields);
+ all_fields.extend(set_fields);
+ all_fields.extend(map_fields);
+ all_fields.extend(other_fields);
+
+ all_fields.into_iter().map(|(name, _, _)| name).collect()
+}
+pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
+ let sorted_names = get_sorted_field_names(fields);
+ let names = sorted_names.iter().map(|name| {
+ quote! { #name }
+ });
quote! {
- let sorted_field_names = {
- #all_primitive_declare
- #final_declare
- #other_declare
- #container_declare
-
- #trait_object_fields_ts
- #group_sort_enum_other_fields
-
- let mut sorted_field_names: Vec<String> =
Vec::with_capacity(#fields_len);
- #all_primitive_extend
- #final_extend
- #other_extend
- #container_extend
-
- sorted_field_names
- };
+ &[#(#names),*]
}
}
+
+pub(crate) fn skip_ref_flag(ty: &Type) -> bool {
+ // !T::fory_is_option() && PRIMITIVE_TYPES.contains(&elem_type_id)
+ PRIMITIVE_TYPE_NAMES.contains(&extract_type_name(ty).as_str())
+}
diff --git a/rust/fory-derive/src/object/write.rs
b/rust/fory-derive/src/object/write.rs
index 6adba6780..5736f6aad 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -16,7 +16,8 @@
// under the License.
use super::util::{
- classify_trait_object_field, create_wrapper_types_arc,
create_wrapper_types_rc, StructField,
+ classify_trait_object_field, create_wrapper_types_arc,
create_wrapper_types_rc, skip_ref_flag,
+ StructField,
};
use proc_macro2::TokenStream;
use quote::quote;
@@ -98,15 +99,13 @@ pub fn gen_write_type_info() -> TokenStream {
}
}
-fn gen_write_match_arm(field: &Field) -> TokenStream {
+fn gen_write_field(field: &Field) -> TokenStream {
let ty = &field.ty;
let ident = &field.ident;
- let name_str = ident.as_ref().unwrap().to_string();
-
match classify_trait_object_field(ty) {
StructField::BoxDyn(_) => {
quote! {
- #name_str => {
+ {
let any_ref = self.#ident.as_any();
let concrete_type_id = any_ref.type_id();
let fory_type_id = fory.get_type_resolver()
@@ -131,7 +130,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper = #wrapper_ty::from(self.#ident.clone() as
std::rc::Rc<dyn #trait_ident>);
fory_core::serializer::Serializer::fory_write(&wrapper,
fory, context, true);
}
@@ -142,7 +141,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper = #wrapper_ty::from(self.#ident.clone() as
std::sync::Arc<dyn #trait_ident>);
fory_core::serializer::Serializer::fory_write(&wrapper,
fory, context, true);
}
@@ -153,7 +152,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
.map(|item| #wrapper_ty::from(item.clone() as
std::rc::Rc<dyn #trait_ident>))
.collect();
@@ -166,7 +165,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper_vec: Vec<#wrapper_ty> = self.#ident.iter()
.map(|item| #wrapper_ty::from(item.clone() as
std::sync::Arc<dyn #trait_ident>))
.collect();
@@ -179,7 +178,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = self.#ident.iter()
.map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone()
as std::rc::Rc<dyn #trait_ident>)))
.collect();
@@ -192,7 +191,7 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
let wrapper_ty = types.wrapper_ty;
let trait_ident = types.trait_ident;
quote! {
- #name_str => {
+ {
let wrapper_map: std::collections::HashMap<#key_ty,
#wrapper_ty> = self.#ident.iter()
.map(|(k, v)| (k.clone(), #wrapper_ty::from(v.clone()
as std::sync::Arc<dyn #trait_ident>)))
.collect();
@@ -202,63 +201,28 @@ fn gen_write_match_arm(field: &Field) -> TokenStream {
}
StructField::Forward => {
quote! {
- #name_str => {
+ {
fory_core::serializer::Serializer::fory_write(&self.#ident, fory, context,
true);
}
}
}
_ => {
+ let skip_ref_flag = skip_ref_flag(ty);
quote! {
- #name_str => {
- let skip_ref_flag =
fory_core::serializer::get_skip_ref_flag::<#ty>(fory);
-
fory_core::serializer::write_ref_info_data::<#ty>(&self.#ident, fory, context,
true, skip_ref_flag, false);
- }
+
fory_core::serializer::write_ref_info_data::<#ty>(&self.#ident, fory, context,
true, #skip_ref_flag, false);
}
}
}
}
pub fn gen_write_data(fields: &[&Field]) -> TokenStream {
- let sorted_serialize = if fields.is_empty() {
+ if fields.is_empty() {
quote! {}
} else {
- let match_ts: Vec<_> = fields
- .iter()
- .map(|field| gen_write_match_arm(field))
- .collect();
- #[cfg(not(feature = "fields-loop-unroll"))]
- let loop_ts = quote! {
- for field_name in sorted_field_names {
- match field_name.as_str() {
- #(#match_ts),*
- , _ => {unreachable!()}
- }
- }
- };
- #[cfg(feature = "fields-loop-unroll")]
- let loop_ts = {
- let loop_item_ts = fields.iter().enumerate().map(|(i, _field)| {
- let idx = syn::Index::from(i);
- quote! {
- let field_name = sorted_field_names.get(#idx).unwrap();
- match field_name.as_str() {
- #(#match_ts),*
- , _ => { unreachable!() }
- }
- }
- });
- quote! {
- #(#loop_item_ts)*
- }
- };
+ let write_fields_ts: Vec<_> = fields.iter().map(|field|
gen_write_field(field)).collect();
quote! {
- let sorted_field_names = <Self as
fory_core::serializer::StructSerializer>::fory_get_sorted_field_names(fory);
- let sorted_field_names = sorted_field_names.as_ref();
- #loop_ts
+ #(#write_fields_ts)*
}
- };
- quote! {
- #sorted_serialize
}
}
diff --git a/rust/fory-derive/src/util.rs b/rust/fory-derive/src/util.rs
index edb012171..6990fc776 100644
--- a/rust/fory-derive/src/util.rs
+++ b/rust/fory-derive/src/util.rs
@@ -18,9 +18,23 @@
use syn::{Field, Fields, GenericArgument, PathArguments, Type, TypePath,
TypeTraitObject};
pub fn sorted_fields(fields: &Fields) -> Vec<&Field> {
- let mut fields = fields.iter().collect::<Vec<&Field>>();
- fields.sort_by(|a, b| a.ident.cmp(&b.ident));
- fields
+ let fields = fields.iter().collect::<Vec<&Field>>();
+ get_sorted_fields(&fields)
+}
+
+pub fn get_sorted_fields<'a>(fields: &[&'a Field]) -> Vec<&'a Field> {
+ use crate::object::util::get_sorted_field_names;
+
+ let sorted_names = get_sorted_field_names(fields);
+ let mut sorted_fields = Vec::with_capacity(fields.len());
+
+ for name in &sorted_names {
+ if let Some(field) = fields.iter().find(|f| *f.ident.as_ref().unwrap()
== name) {
+ sorted_fields.push(*field);
+ }
+ }
+
+ sorted_fields
}
/// Check if a type is `Box<dyn Trait>` and return the trait type and trait
name if it is
diff --git a/rust/tests/tests/test_simple_struct.rs
b/rust/tests/tests/test_simple_struct.rs
new file mode 100644
index 000000000..438554a1e
--- /dev/null
+++ b/rust/tests/tests/test_simple_struct.rs
@@ -0,0 +1,72 @@
+// 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.
+
+use fory_core::fory::Fory;
+use fory_core::types::Mode::Compatible;
+use fory_derive::ForyObject;
+use std::collections::HashMap;
+
+#[test]
+fn test_simple() {
+ // a single test for cargo expand and analysis
+ // &["f7", "last", "f2", "f5", "f3", "f6", "f1"]
+ #[derive(ForyObject, Debug)]
+ struct Animal1 {
+ f1: HashMap<i8, Vec<i8>>,
+ f2: String,
+ f3: Vec<i8>,
+ f5: String,
+ f6: Vec<i8>,
+ f7: i8,
+ last: i8,
+ }
+
+ // &["f7", "f5", "last", "f4", "f3", "f6", "f1"]
+ #[derive(ForyObject, Debug)]
+ struct Animal2 {
+ f1: HashMap<i8, Vec<i8>>,
+ f3: Vec<i8>,
+ f4: String,
+ f5: i8,
+ f6: Vec<i16>,
+ f7: i16,
+ last: i8,
+ }
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Animal1>(999);
+ fory2.register::<Animal2>(999);
+ let animal: Animal1 = Animal1 {
+ f1: HashMap::from([(1, vec![2])]),
+ f2: String::from("hello"),
+ f3: vec![1, 2, 3],
+ f5: String::from("f5"),
+ f6: vec![42],
+ f7: 43,
+ last: 44,
+ };
+
+ let bin = fory1.serialize(&animal);
+ let obj: Animal2 = fory2.deserialize(&bin).unwrap();
+ assert_eq!(animal.f1, obj.f1);
+ assert_eq!(animal.f3, obj.f3);
+ assert_eq!(obj.f4, String::default());
+ assert_eq!(obj.f5, i8::default());
+ assert_eq!(obj.f6, Vec::<i16>::default());
+ assert_eq!(obj.f7, i16::default());
+ assert_eq!(animal.last, obj.last);
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]