-
Notifications
You must be signed in to change notification settings - Fork 362
feat: add unsigned number for dart #3144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Hey @chaokunyang |
|
@ayush00git It's great to add wrapper with overlaoed numeric compute operator. But protobuf and flatfbuffer use int type instead, could we also support annotation based approach like we did in java: https://fory.apache.org/docs/next/guide/java/field_configuration#unsigned-integers Support both approaches will provide maximal flexibility to our users |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for unsigned integer types in Dart to enable cross-language serialization compatibility. Since Dart only has 64-bit signed integers, these wrapper classes provide proper unsigned integer semantics with automatic overflow/underflow wrapping behavior.
Changes:
- Added UInt8, UInt16, and UInt32 wrapper classes with automatic wrapping and full operator support
- Implemented 7 new serializers for unsigned types (UINT8, UINT16, UINT32, VAR_UINT32, UINT64, VAR_UINT64, TAGGED_UINT64)
- Added comprehensive test coverage for all unsigned type behaviors
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
dart/packages/fory/lib/src/datatype/uint8.dart |
New UInt8 class with 8-bit unsigned integer wrapping logic and operator overloads |
dart/packages/fory/lib/src/datatype/uint16.dart |
New UInt16 class with 16-bit unsigned integer wrapping logic and operator overloads |
dart/packages/fory/lib/src/datatype/uint32.dart |
New UInt32 class with 32-bit unsigned integer wrapping logic and operator overloads |
dart/packages/fory/lib/src/const/obj_type.dart |
Added 7 new unsigned type enum values (UINT8-TAGGED_UINT64) and updated ID range check |
dart/packages/fory/lib/src/serializer/primitive_type_serializer.dart |
Implemented 7 serializers with caching for all unsigned integer types |
dart/packages/fory-test/test/datatype_test/uint_test.dart |
Added comprehensive tests for UInt8, UInt16, UInt32 covering wrapping, operators, and edge cases |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
@chaokunyang |
|
Hey @chaokunyang |
You may need to dive into the code, I don't write dart. |
chaokunyang
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, please add unsgined number support to struct serializer in a following-up PR
sure, i'll just start working on this one.. |
…3155) ## Why? While unsigned integer types (UInt8, UInt16, UInt32) were added to Fory in PR #3144 with their primitive serializers and wrapper classes, they were not integrated into the framework's type system for struct serialization. This meant users could serialize individual UInt values but could not use them as fields in structs annotated with `@ForyClass`. For example: * A struct with `UInt16 port` field would fail during code generation * Cross-language data structures using unsigned integers couldn't be represented in Dart structs * The type system didn't recognize UInt types as valid field types for serialization ## What does this PR do? ### 1. Registers Unsigned Integer Types in DartTypeEnum Added UInt8, UInt16, and UInt32 to the type enumeration in `dart_type.dart`: ```dart UINT8(UInt8, true, 'UInt8', 'package', 'fory/src/datatype/uint8.dart', ObjType.UINT8, true, 'dart:core@UInt8'), UINT16(UInt16, true, 'UInt16', 'package', 'fory/src/datatype/uint16.dart', ObjType.UINT16, true, 'dart:core@UInt16'), UINT32(UInt32, true, 'UInt32', 'package', 'fory/src/datatype/uint32.dart', ObjType.UINT32, true, 'dart:core@UInt32'), ``` This enables the code generator to recognize unsigned types during static analysis and generate proper serialization metadata. ### 2. Integrates Serializers into SerializerPool Registered the serializers in the framework initialization: ```dart type2Ser[UInt8]!.ser = UInt8Serializer.cache.getSerializer(conf); type2Ser[UInt16]!.ser = UInt16Serializer.cache.getSerializer(conf); type2Ser[UInt32]!.ser = UInt32Serializer.cache.getSerializer(conf); ``` This ensures serializers are available when the framework starts and can be used for struct field serialization. ### 3. Adds Comprehensive Test Suite for Struct Serialization Created test entity and test cases: ```dart @ForyClass(promiseAcyclic: true) class UIntStruct with _$UIntStructFory { final UInt8 age; final UInt16 port; final UInt32 count; const UIntStruct(this.age, this.port, this.count); } ``` **Test coverage:** * Normal value serialization/deserialization * Maximum boundary values (255, 65535, 4294967295) * Minimum boundary values (0, 0, 0) * Round-trip serialization integrity **Run tests:** ```bash cd packages/fory-test dart test test/struct_test/uint_struct_test.dart ``` ## Related issues Completes the unsigned integer types support initiated in PR #3144. ## Does this PR introduce any user-facing change? * [x] Does this PR introduce any public API change? * **Dart**: UInt8, UInt16, UInt32 can now be used as fields in `@ForyClass` structs * The wrapper classes themselves were already public (added in #3144) * This PR enables their use in code generation and struct serialization contexts * [ ] Does this PR introduce any binary protocol compatibility change? * No changes to binary encoding format * Uses existing serializers from #3144 (UInt8: 1 byte, UInt16: 2 bytes LE, UInt32: 4 bytes LE) * Type IDs remain the same: UINT8 (40), UINT16 (41), UINT32 (42) ## Benchmark N/A, The serializers themselves were already implemented in #3144. This PR only adds registration overhead during framework initialization, which is negligible..
…3181) ## Why? While unsigned integer type annotations (`@Uint8Type`, `@Uint16Type`, `@Uint32Type`, `@Uint64Type`) were added in PR #3144, they were not integrated into the code generation system. This meant: * The annotation definitions existed but were not recognized by the code generator * No support for annotation-based type specification like `@Uint8Type() int age` in struct fields * Users couldn't use the protobuf/flatbuffer-style ergonomic API that was intended * No way to specify encoding variants (fixed vs varint) for uint32/uint64 via annotations during code generation ## What does this PR do? ### 1. Created Uint Annotation Analyzer Added `uint_annotation_analyzer.dart` to detect and parse uint type annotations during code generation: ```dart class UintAnnotationAnalyzer { UintAnnotationResult analyze( List<ElementAnnotation> metadata, LocationMark locationMark, ) { // Detects @uint8type, @Uint16Type, @Uint32Type, @Uint64Type // Extracts encoding options (fixed, varint, tagged) // Returns appropriate ObjType } } ``` **Supported annotations:** * `@Uint8Type()` → `ObjType.UINT8` * `@Uint16Type()` → `ObjType.UINT16` * `@Uint32Type()` → `ObjType.UINT32` * `@Uint32Type(encoding: UintEncoding.varint)` → `ObjType.VAR_UINT32` * `@Uint64Type()` → `ObjType.UINT64` * `@Uint64Type(encoding: UintEncoding.varint)` → `ObjType.VAR_UINT64` * `@Uint64Type(encoding: UintEncoding.tagged)` → `ObjType.TAGGED_UINT64` ### 2. Extended Type Identifier System Updated `analysis_type_identifier.dart` to recognize uint annotation types: ```dart static final List<Type3StringKey> _keys = [ // ... existing annotations Type3StringKey('Uint8Type', 'package', 'fory/src/annotation/uint_types.dart'), Type3StringKey('Uint16Type', 'package', 'fory/src/annotation/uint_types.dart'), Type3StringKey('Uint32Type', 'package', 'fory/src/annotation/uint_types.dart'), Type3StringKey('Uint64Type', 'package', 'fory/src/annotation/uint_types.dart'), ]; ``` ### 3. Integrated Annotation-Based Type Override Modified `type_analyzer_impl.dart` to support annotation-based type override: ```dart TypeSpecGen getTypeImmutableAndTagWithOverride( TypeDecision typeDecision, LocationMark locationMark, ObjType objTypeOverride, ) { // Uses annotation-specified ObjType instead of default type resolution } ``` ### 4. Updated Field Analyzer Modified `field_analyzer_impl.dart` to check for uint annotations: ```dart // Check for uint annotations final uintAnnotationResult = Analyzer.uintAnnotationAnalyzer.analyze( element.metadata, locationMark, ); if (uintAnnotationResult.hasAnnotation) { // Use annotation-based type override fieldType = Analyzer.typeAnalyzer.getTypeImmutableAndTagWithOverride( typeDecision, locationMark, uintAnnotationResult.objType!, ); } ``` ## Related issues Completes the unsigned integer annotation types support initiated in PR #3144 by integrating the annotations into the code generation system. ## Does this PR introduce any user-facing change? * [x] Does this PR introduce any public API change? * **Dart**: Users can now use `@Uint8Type()`, `@Uint16Type()`, `@Uint32Type()`, `@Uint64Type()` annotations on native `int` fields in `@ForyClass` structs * Enables encoding variant specification via `encoding` parameter for uint32/uint64 * Provides more ergonomic API: `@Uint8Type() int age` instead of `UInt8 age` * [ ] Does this PR introduce any binary protocol compatibility change? * No changes to binary encoding format * Uses existing ObjType mappings and serializers * Type IDs remain the same: UINT8 (40), UINT16 (41), UINT32 (42), VAR_UINT32 (43), UINT64 (44), VAR_UINT64 (45), TAGGED_UINT64 (46) ## Benchmark N/A - This PR only adds annotation processing during code generation (build-time). No runtime performance impact. --------- Co-authored-by: Shawn Yang <[email protected]>
Why?
Dart doesn't have native unsigned integer types (only a single 64-bit signed int but many other languages (Rust, Go, C++, JavaScript) do. When serializing data across languages, we need to properly handle unsigned integers to ensure correct values and efficient encoding.
For example:
u32with value3_000_000_000cannot be directly represented in Dart without proper unsigned handlingWhat does this PR do?
1. Adds Unsigned Integer Type Support (Dart)
New type IDs:
UINT8(40),UINT16(41),UINT32(42),VAR_UINT32(43),UINT64(44),VAR_UINT64(45),TAGGED_UINT64(46)Unsigned types use the same bit width as signed types but interpret values in the unsigned range.
2. Dart: Adds Wrapper Classes for Fixed-Size Unsigned Integers
Created
UInt8UInt16andUInt32wrapper classes that extendFixedNum:Features:
Core wrapping logic:
3. Dart: Adds Serializers for All Unsigned Types
Implemented 7 serializer classes following the existing cache pattern:
ByteReader/Writer methods used:
readUint8/16/32(),writeUint8/16/32()readVarUint32(),writeVarUint32()readUint64(),writeUint64()readVarInt64(),writeVarInt64()4. Dart: Adds Comprehensive Test Suite
Created test coverage for all unsigned types:
Run tests:
Related Issue
Does this PR introduce any user-facing change?
Does this PR introduce any public API change?
UInt8,UInt16,UInt32ObjType:UINT8,UINT16,UINT32,VAR_UINT32,UINT64,VAR_UINT64,TAGGED_UINT64Does this PR introduce any binary protocol compatibility change?
Benchmark
N/A - This PR focuses on correctness and cross-language compatibility. Performance characteristics of unsigned types are similar to their signed counterparts.