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 370addbfc refactor(dart): aligned dart internal implementation (#3601)
370addbfc is described below
commit 370addbfc59398f0b83a60f6e23213cfb710c24b
Author: Geethapranay1 <[email protected]>
AuthorDate: Wed Apr 22 08:32:04 2026 +0530
refactor(dart): aligned dart internal implementation (#3601)
## Why?
The dart implementation's internal collections and map serializers were
previously structured differently than the java and c# runtimes, using
raw hex bitmasks and monolithic methods which made reading the code
difficult.
## What does this PR do?
- extracts chunk/header magic numbers into `MapFlags` and
`CollectionFlags`.
- breaks down `ListSerializer.writePayload` into
`_writeSameTypeElements` and `_writeDifferentTypeElements`.
- adds identical depth getters to writecontext and readcontext.
- maintained dart proper `maxDepth` spec enforcement on both read/write
bounds.
## Related issues
Fixes #3595
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
.../fory/lib/src/context/read_context.dart | 2 +
.../fory/lib/src/context/write_context.dart | 2 +
.../lib/src/serializer/collection_serializers.dart | 414 +++++++++++----------
.../fory/lib/src/serializer/map_serializers.dart | 53 +--
4 files changed, 260 insertions(+), 211 deletions(-)
diff --git a/dart/packages/fory/lib/src/context/read_context.dart
b/dart/packages/fory/lib/src/context/read_context.dart
index d2c5a6d54..1945e5ae0 100644
--- a/dart/packages/fory/lib/src/context/read_context.dart
+++ b/dart/packages/fory/lib/src/context/read_context.dart
@@ -124,6 +124,8 @@ final class ReadContext {
}
return value;
}
+
+ int get depth => _depth;
/// Records entry into one more nested read frame.
void increaseDepth() {
diff --git a/dart/packages/fory/lib/src/context/write_context.dart
b/dart/packages/fory/lib/src/context/write_context.dart
index 2808f031d..f5fec0e48 100644
--- a/dart/packages/fory/lib/src/context/write_context.dart
+++ b/dart/packages/fory/lib/src/context/write_context.dart
@@ -105,6 +105,8 @@ final class WriteContext {
set structWriteSlots(StructWriteSlots? value) {
_structWriteSlots = value;
}
+
+ int get depth => _depth;
/// Records entry into one more nested write frame.
void increaseDepth() {
diff --git a/dart/packages/fory/lib/src/serializer/collection_serializers.dart
b/dart/packages/fory/lib/src/serializer/collection_serializers.dart
index 828a6ac77..d4d59a6ea 100644
--- a/dart/packages/fory/lib/src/serializer/collection_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/collection_serializers.dart
@@ -28,6 +28,13 @@ import 'package:fory/src/serializer/scalar_serializers.dart';
import 'package:fory/src/serializer/serializer.dart';
import 'package:fory/src/serializer/serializer_support.dart';
+abstract final class CollectionFlags {
+ static const int trackingRef = 0x01;
+ static const int hasNull = 0x02;
+ static const int isDeclaredElementType = 0x04;
+ static const int isSameType = 0x08;
+}
+
@pragma('vm:prefer-inline')
void _writeDirectTypeInfoValue(
WriteContext context,
@@ -277,26 +284,19 @@ final class ListSerializer extends Serializer<List> {
elementFieldType!,
declaredTypeInfo,
);
- final analysis = _analyzeListHeader(
+ final analysis = _analyzeElementsHeader(
context,
values,
usesDeclaredType: usesDeclaredType,
);
final elementTrackRef = (elementFieldType?.ref ?? false) ||
(elementFieldType == null && trackRef);
- var header = 0;
- if (elementTrackRef) {
- header |= 0x01;
- }
- if (analysis.hasNull) {
- header |= 0x02;
- }
- if (usesDeclaredType) {
- header |= 0x04;
- }
- if (analysis.sameType) {
- header |= 0x08;
- }
+ final header = _buildCollectionHeader(
+ trackRef: elementTrackRef,
+ hasNull: analysis.hasNull,
+ usesDeclaredType: usesDeclaredType,
+ sameType: analysis.sameType,
+ );
context.buffer.writeUint8(header);
final sameTypeInfo =
!usesDeclaredType && analysis.sameType ? analysis.sameTypeInfo : null;
@@ -309,129 +309,33 @@ final class ListSerializer extends Serializer<List> {
);
}
if (declaredTypeInfo != null) {
- final tracksDepth = tracksNestedPayloadDepth(declaredTypeInfo);
- if (tracksDepth) {
- context.increaseDepth();
- }
- for (final value in values) {
- if (value == null) {
- context.buffer.writeByte(RefWriter.nullFlag);
- continue;
- }
- if (elementTrackRef) {
- writeTypeInfoValue(
- context,
- declaredTypeInfo,
- elementFieldType,
- value as Object,
- trackRef: true,
- );
- } else if (analysis.hasNull) {
- context.buffer.writeByte(RefWriter.notNullValueFlag);
- _writeDirectTypeInfoValue(
- context,
- declaredTypeInfo,
- elementFieldType,
- value as Object,
- );
- } else {
- _writeDirectTypeInfoValue(
- context,
- declaredTypeInfo,
- elementFieldType,
- value as Object,
- );
- }
- }
- if (tracksDepth) {
- context.decreaseDepth();
- }
+ _writeSameTypeElements(
+ context,
+ values,
+ declaredTypeInfo,
+ elementFieldType,
+ elementTrackRef,
+ analysis.hasNull,
+ );
return;
}
if (sameTypeInfo != null) {
- final tracksDepth = tracksNestedPayloadDepth(sameTypeInfo);
- if (tracksDepth) {
- context.increaseDepth();
- }
- for (final value in values) {
- if (value == null) {
- context.buffer.writeByte(RefWriter.nullFlag);
- } else if (elementTrackRef) {
- writeTypeInfoValue(
- context,
- sameTypeInfo,
- null,
- value as Object,
- trackRef: true,
- );
- } else if (analysis.hasNull) {
- context.buffer.writeByte(RefWriter.notNullValueFlag);
- _writeDirectTypeInfoValue(
- context,
- sameTypeInfo,
- null,
- value as Object,
- );
- } else {
- _writeDirectTypeInfoValue(
- context,
- sameTypeInfo,
- null,
- value as Object,
- );
- }
- }
- if (tracksDepth) {
- context.decreaseDepth();
- }
+ _writeSameTypeElements(
+ context,
+ values,
+ sameTypeInfo,
+ usesDeclaredType ? elementFieldType : null,
+ elementTrackRef,
+ analysis.hasNull,
+ );
return;
}
- for (final value in values) {
- if (analysis.sameType && analysis.sameTypeInfo != null) {
- if (value == null) {
- context.buffer.writeByte(RefWriter.nullFlag);
- } else if (elementTrackRef) {
- final handled = context.refWriter.writeRefOrNull(
- context.buffer,
- value,
- trackRef: analysis.sameTypeInfo!.supportsRef,
- );
- if (!handled) {
- context.writeResolvedValue(
- analysis.sameTypeInfo!,
- value as Object,
- null,
- );
- }
- } else if (analysis.hasNull) {
- context.buffer.writeByte(RefWriter.notNullValueFlag);
- context.writeResolvedValue(
- analysis.sameTypeInfo!,
- value as Object,
- null,
- );
- } else {
- context.writeResolvedValue(
- analysis.sameTypeInfo!,
- value as Object,
- null,
- );
- }
- continue;
- }
- if (elementTrackRef) {
- context.writeRef(value);
- } else if (analysis.hasNull) {
- if (value == null) {
- context.buffer.writeByte(RefWriter.nullFlag);
- } else {
- context.buffer.writeByte(RefWriter.notNullValueFlag);
- context.writeNonRef(value as Object);
- }
- } else {
- context.writeNonRef(value as Object);
- }
- }
+ _writeDifferentTypeElements(
+ context,
+ values,
+ analysis,
+ elementTrackRef,
+ );
}
static List<Object?> readPayload(
@@ -527,6 +431,127 @@ Set<T> readTypedSetPayload<T>(
return Set<T>.of(readTypedListPayload(context, elementFieldType, convert));
}
+int _buildCollectionHeader({
+ required bool trackRef,
+ required bool hasNull,
+ required bool usesDeclaredType,
+ required bool sameType,
+}) {
+ var header = 0;
+ if (trackRef) {
+ header |= CollectionFlags.trackingRef;
+ }
+ if (hasNull) {
+ header |= CollectionFlags.hasNull;
+ }
+ if (usesDeclaredType) {
+ header |= CollectionFlags.isDeclaredElementType;
+ }
+ if (sameType) {
+ header |= CollectionFlags.isSameType;
+ }
+ return header;
+}
+
+void _writeSameTypeElements(
+ WriteContext context,
+ Iterable values,
+ TypeInfo typeInfo,
+ FieldType? fieldType,
+ bool trackRef,
+ bool hasNull,
+) {
+ final tracksDepth = tracksNestedPayloadDepth(typeInfo);
+ if (tracksDepth) {
+ context.increaseDepth();
+ }
+ for (final value in values) {
+ if (value == null) {
+ context.buffer.writeByte(RefWriter.nullFlag);
+ } else if (trackRef) {
+ writeTypeInfoValue(
+ context,
+ typeInfo,
+ fieldType,
+ value as Object,
+ trackRef: true,
+ );
+ } else if (hasNull) {
+ context.buffer.writeByte(RefWriter.notNullValueFlag);
+ _writeDirectTypeInfoValue(
+ context,
+ typeInfo,
+ fieldType,
+ value as Object,
+ );
+ } else {
+ _writeDirectTypeInfoValue(
+ context,
+ typeInfo,
+ fieldType,
+ value as Object,
+ );
+ }
+ }
+ if (tracksDepth) {
+ context.decreaseDepth();
+ }
+}
+
+void _writeDifferentTypeElements(
+ WriteContext context,
+ Iterable values,
+ _ElementsHeaderAnalysis analysis,
+ bool trackRef,
+) {
+ for (final value in values) {
+ if (analysis.sameType && analysis.sameTypeInfo != null) {
+ if (value == null) {
+ context.buffer.writeByte(RefWriter.nullFlag);
+ } else if (trackRef) {
+ final handled = context.refWriter.writeRefOrNull(
+ context.buffer,
+ value,
+ trackRef: analysis.sameTypeInfo!.supportsRef,
+ );
+ if (!handled) {
+ context.writeResolvedValue(
+ analysis.sameTypeInfo!,
+ value as Object,
+ null,
+ );
+ }
+ } else if (analysis.hasNull) {
+ context.buffer.writeByte(RefWriter.notNullValueFlag);
+ context.writeResolvedValue(
+ analysis.sameTypeInfo!,
+ value as Object,
+ null,
+ );
+ } else {
+ context.writeResolvedValue(
+ analysis.sameTypeInfo!,
+ value as Object,
+ null,
+ );
+ }
+ continue;
+ }
+ if (trackRef) {
+ context.writeRef(value);
+ } else if (analysis.hasNull) {
+ if (value == null) {
+ context.buffer.writeByte(RefWriter.nullFlag);
+ } else {
+ context.buffer.writeByte(RefWriter.notNullValueFlag);
+ context.writeNonRef(value as Object);
+ }
+ } else {
+ context.writeNonRef(value as Object);
+ }
+ }
+}
+
final class _PreparedListRead {
final int size;
final bool trackRef;
@@ -572,10 +597,11 @@ _PreparedListRead _prepareListRead(
);
}
final header = context.buffer.readUint8();
- final trackRef = (header & 0x01) == 1;
- final hasNull = (header & 0x02) != 0;
- final usesDeclaredType = (header & 0x04) != 0;
- final sameType = (header & 0x08) != 0;
+ final trackRef = (header & CollectionFlags.trackingRef) != 0;
+ final hasNull = (header & CollectionFlags.hasNull) != 0;
+ final usesDeclaredType =
+ (header & CollectionFlags.isDeclaredElementType) != 0;
+ final sameType = (header & CollectionFlags.isSameType) != 0;
final declaredTypeInfo = usesDeclaredType && elementFieldType != null
? context.typeResolver.resolveFieldType(
elementFieldType.withRootOverrides(nullable: hasNull, ref: trackRef),
@@ -604,58 +630,22 @@ Object? _readPreparedListItem(
_PreparedListRead state,
) {
if (state.declaredTypeInfo != null) {
- if (state.hasNull || state.trackRef) {
- final flag = context.refReader.tryPreserveRefId(context.buffer);
- final preservedRefId = flag >= RefWriter.refValueFlag ? flag : null;
- if (flag == RefWriter.nullFlag) {
- return null;
- }
- if (flag == RefWriter.refFlag) {
- return context.refReader.getReadRef();
- }
- final value = readTypeInfoValue(
- context,
- state.declaredTypeInfo!,
- state.elementFieldType,
- hasPreservedRef: preservedRefId != null,
- );
- if (preservedRefId != null &&
- state.declaredTypeInfo!.supportsRef &&
- context.refReader.readRefAt(preservedRefId) == null) {
- context.refReader.setReadRef(preservedRefId, value);
- }
- return value;
- }
- return readTypeInfoValue(
+ return _readSameTypeElement(
context,
state.declaredTypeInfo!,
state.elementFieldType,
+ state.trackRef,
+ state.hasNull,
);
}
if (state.sameTypeInfo != null) {
- if (state.hasNull || state.trackRef) {
- final flag = context.refReader.tryPreserveRefId(context.buffer);
- final preservedRefId = flag >= RefWriter.refValueFlag ? flag : null;
- if (flag == RefWriter.nullFlag) {
- return null;
- }
- if (flag == RefWriter.refFlag) {
- return context.refReader.getReadRef();
- }
- final value = readTypeInfoValue(
- context,
- state.sameTypeInfo!,
- null,
- hasPreservedRef: preservedRefId != null,
- );
- if (preservedRefId != null &&
- state.sameTypeInfo!.supportsRef &&
- context.refReader.readRefAt(preservedRefId) == null) {
- context.refReader.setReadRef(preservedRefId, value);
- }
- return value;
- }
- return readTypeInfoValue(context, state.sameTypeInfo!, null);
+ return _readSameTypeElement(
+ context,
+ state.sameTypeInfo!,
+ null,
+ state.trackRef,
+ state.hasNull,
+ );
}
if (state.usesDeclaredType && state.elementFieldType != null) {
return readFieldTypeValue<Object?>(
@@ -689,10 +679,56 @@ Object? _readPreparedListItem(
}
return context.readResolvedValue(state.sameTypeInfo!, null);
}
- if (state.trackRef) {
+ return _readDifferentTypeElement(
+ context,
+ state.trackRef,
+ state.hasNull,
+ );
+}
+
+@pragma('vm:prefer-inline')
+Object? _readSameTypeElement(
+ ReadContext context,
+ TypeInfo typeInfo,
+ FieldType? fieldType,
+ bool trackRef,
+ bool hasNull,
+) {
+ if (hasNull || trackRef) {
+ final flag = context.refReader.tryPreserveRefId(context.buffer);
+ final preservedRefId = flag >= RefWriter.refValueFlag ? flag : null;
+ if (flag == RefWriter.nullFlag) {
+ return null;
+ }
+ if (flag == RefWriter.refFlag) {
+ return context.refReader.getReadRef();
+ }
+ final value = readTypeInfoValue(
+ context,
+ typeInfo,
+ fieldType,
+ hasPreservedRef: preservedRefId != null,
+ );
+ if (preservedRefId != null &&
+ typeInfo.supportsRef &&
+ context.refReader.readRefAt(preservedRefId) == null) {
+ context.refReader.setReadRef(preservedRefId, value);
+ }
+ return value;
+ }
+ return readTypeInfoValue(context, typeInfo, fieldType);
+}
+
+@pragma('vm:prefer-inline')
+Object? _readDifferentTypeElement(
+ ReadContext context,
+ bool trackRef,
+ bool hasNull,
+) {
+ if (trackRef) {
return context.readRef();
}
- if (state.hasNull) {
+ if (hasNull) {
return context.readNullable();
}
return context.readNonRef();
@@ -720,7 +756,7 @@ void writeTypeInfoValue(
}
}
-_ListHeaderAnalysis _analyzeListHeader(
+_ElementsHeaderAnalysis _analyzeElementsHeader(
WriteContext context,
Iterable values, {
required bool usesDeclaredType,
@@ -733,7 +769,7 @@ _ListHeaderAnalysis _analyzeListHeader(
break;
}
}
- return _ListHeaderAnalysis(
+ return _ElementsHeaderAnalysis(
hasNull: hasNull,
sameType: true,
sameTypeInfo: null,
@@ -762,7 +798,7 @@ _ListHeaderAnalysis _analyzeListHeader(
sameType = false;
}
}
- return _ListHeaderAnalysis(
+ return _ElementsHeaderAnalysis(
hasNull: hasNull,
sameType: sameType,
sameTypeInfo: sameTypeInfo,
@@ -770,13 +806,13 @@ _ListHeaderAnalysis _analyzeListHeader(
);
}
-final class _ListHeaderAnalysis {
+final class _ElementsHeaderAnalysis {
final bool hasNull;
final bool sameType;
final TypeInfo? sameTypeInfo;
final Object? firstNonNull;
- const _ListHeaderAnalysis({
+ const _ElementsHeaderAnalysis({
required this.hasNull,
required this.sameType,
required this.sameTypeInfo,
diff --git a/dart/packages/fory/lib/src/serializer/map_serializers.dart
b/dart/packages/fory/lib/src/serializer/map_serializers.dart
index 784924b89..57ae79030 100644
--- a/dart/packages/fory/lib/src/serializer/map_serializers.dart
+++ b/dart/packages/fory/lib/src/serializer/map_serializers.dart
@@ -26,6 +26,15 @@ import 'package:fory/src/resolver/type_resolver.dart';
import 'package:fory/src/serializer/collection_serializers.dart';
import 'package:fory/src/serializer/serializer.dart';
+abstract final class MapFlags {
+ static const int trackingKeyRef = 0x01;
+ static const int keyHasNull = 0x02;
+ static const int keyDeclaredType = 0x04;
+ static const int trackingValueRef = 0x08;
+ static const int valueHasNull = 0x10;
+ static const int valueDeclaredType = 0x20;
+}
+
final class MapSerializer extends Serializer<Map> {
const MapSerializer();
@@ -272,8 +281,8 @@ Map<K, V> readTypedMapPayload<K, V>(
}
while (remaining > 0) {
final header = context.buffer.readUint8();
- final keyHasNull = (header & 0x02) != 0;
- final valueHasNull = (header & 0x10) != 0;
+ final keyHasNull = (header & MapFlags.keyHasNull) != 0;
+ final valueHasNull = (header & MapFlags.valueHasNull) != 0;
if (keyHasNull || valueHasNull) {
result[convertKey(
_readNullChunkKey(
@@ -293,10 +302,10 @@ Map<K, V> readTypedMapPayload<K, V>(
remaining -= 1;
continue;
}
- final keyTrackRef = (header & 0x01) != 0;
- final valueTrackRef = (header & 0x08) != 0;
- final keyDeclared = (header & 0x04) != 0;
- final valueDeclared = (header & 0x20) != 0;
+ final keyTrackRef = (header & MapFlags.trackingKeyRef) != 0;
+ final valueTrackRef = (header & MapFlags.trackingValueRef) != 0;
+ final keyDeclared = (header & MapFlags.keyDeclaredType) != 0;
+ final valueDeclared = (header & MapFlags.valueDeclaredType) != 0;
final chunkSize = context.buffer.readUint8();
final keyTypeInfo = keyDeclared ? null : context.readTypeMetaValue();
final valueTypeInfo = valueDeclared ? null : context.readTypeMetaValue();
@@ -364,20 +373,20 @@ void _writeNullChunk(
}) {
var header = 0;
if (key == null) {
- header |= 0x02;
+ header |= MapFlags.keyHasNull;
} else if (keyDeclared) {
- header |= 0x04;
+ header |= MapFlags.keyDeclaredType;
}
if (keyTrackRef) {
- header |= 0x01;
+ header |= MapFlags.trackingKeyRef;
}
if (value == null) {
- header |= 0x10;
+ header |= MapFlags.valueHasNull;
} else if (valueDeclared) {
- header |= 0x20;
+ header |= MapFlags.valueDeclaredType;
}
if (valueTrackRef) {
- header |= 0x08;
+ header |= MapFlags.trackingValueRef;
}
context.buffer.writeUint8(header);
if (key != null) {
@@ -445,12 +454,12 @@ Object? _readNullChunkKey(
FieldType? keyFieldType,
TypeInfo? declaredKeyTypeInfo,
) {
- final keyHasNull = (header & 0x02) != 0;
+ final keyHasNull = (header & MapFlags.keyHasNull) != 0;
if (keyHasNull) {
return null;
}
- final trackRef = (header & 0x01) != 0;
- final declared = (header & 0x04) != 0;
+ final trackRef = (header & MapFlags.trackingKeyRef) != 0;
+ final declared = (header & MapFlags.keyDeclaredType) != 0;
if (declared && keyFieldType != null) {
return _readDeclaredMapValue(
context,
@@ -468,12 +477,12 @@ Object? _readNullChunkValue(
FieldType? valueFieldType,
TypeInfo? declaredValueTypeInfo,
) {
- final valueHasNull = (header & 0x10) != 0;
+ final valueHasNull = (header & MapFlags.valueHasNull) != 0;
if (valueHasNull) {
return null;
}
- final trackRef = (header & 0x08) != 0;
- final declared = (header & 0x20) != 0;
+ final trackRef = (header & MapFlags.trackingValueRef) != 0;
+ final declared = (header & MapFlags.valueDeclaredType) != 0;
if (declared && valueFieldType != null) {
return _readDeclaredMapValue(
context,
@@ -545,16 +554,16 @@ int _mapChunkHeader({
}) {
var header = 0;
if (keyTrackRef) {
- header |= 0x01;
+ header |= MapFlags.trackingKeyRef;
}
if (keyDeclared) {
- header |= 0x04;
+ header |= MapFlags.keyDeclaredType;
}
if (valueTrackRef) {
- header |= 0x08;
+ header |= MapFlags.trackingValueRef;
}
if (valueDeclared) {
- header |= 0x20;
+ header |= MapFlags.valueDeclaredType;
}
return header;
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]