Skip to content

Conversation

@ayush00git
Copy link
Contributor

@ayush00git ayush00git commented Jan 13, 2026

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:

  • A Rust u32 with value 3_000_000_000 cannot be directly represented in Dart without proper unsigned handling
  • Variable-length encoding for unsigned integers can skip zigzag encoding overhead
  • Cross-language compatibility requires proper unsigned type support in the protocol

What 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 UInt8 UInt16 and UInt32 wrapper classes that extend FixedNum:

import 'package:fory/src/datatype/uint8.dart';

var value = UInt8(255);
var result = value + UInt8(1);  // Automatically wraps to 0

Features:

  • Automatic overflow/underflow wrapping using bitwise operations
  • Full operator support (arithmetic, bitwise, comparison)
  • Type conversions (toInt, toDouble, toString)

Core wrapping logic:

static int _convert(num value) {
  if (value is int) {
    return value & 0xFF;  // Keeps only lowest 8 bits for UInt8
  }
  return _convert(value.toInt());
}

3. Dart: Adds Serializers for All Unsigned Types

Implemented 7 serializer classes following the existing cache pattern:

final class UInt8Serializer extends Serializer<FixedNum> {
  UInt8 read(ByteReader br, ...) => UInt8(br.readUint8());
  void write(ByteWriter bw, UInt8 v, ...) => bw.writeUint8(v.toInt());
}

ByteReader/Writer methods used:

  • UInt8/16/32: readUint8/16/32(), writeUint8/16/32()
  • VarUInt32: readVarUint32(), writeVarUint32()
  • UInt64: readUint64(), writeUint64()
  • VarUInt64/TaggedUInt64: readVarInt64(), writeVarInt64()

4. Dart: Adds Comprehensive Test Suite

Created test coverage for all unsigned types:

test('wraps on overflow', () {
  var a = UInt8(256);  // Overflow to 0
  var b = UInt8(257);  // Overflow to 1
  var c = UInt8(-1);   // Wraps to 255
  
  expect(a.value, 0);
  expect(b.value, 1);
  expect(c.value, 255);
});

Run tests:

cd dart/packages/fory-test
dart test test/datatype_test/uint_test.dart

Related Issue

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?

    • Dart: New data type classes UInt8, UInt16, UInt32
    • Dart: New enum types in ObjType: UINT8, UINT16, UINT32, VAR_UINT32, UINT64, VAR_UINT64, TAGGED_UINT64
    • Dart: New serializers for all 7 unsigned types
  • Does this PR introduce any binary protocol compatibility change?

    • Adds new type IDs for unsigned integers (40-46)
    • Existing signed integer encoding remains compatible
    • Cross-language compatibility maintained with JavaScript implementation

Benchmark

N/A - This PR focuses on correctness and cross-language compatibility. Performance characteristics of unsigned types are similar to their signed counterparts.

@ayush00git
Copy link
Contributor Author

Hey @chaokunyang
Have a look at the PR and do let me know if there are any chnages.
Thanks : )

@chaokunyang
Copy link
Collaborator

chaokunyang commented Jan 14, 2026

@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

Copy link

Copilot AI left a 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.

@ayush00git
Copy link
Contributor Author

ayush00git commented Jan 14, 2026

@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

@chaokunyang
I'll look into and make the desired changes after looking into the reference.
I'll also add the changes suggested by copilot in the next commits.

@ayush00git
Copy link
Contributor Author

Hey @chaokunyang
I've added the annotation definitions (@Uint8Type, @Uint16Type, @Uint16Type) to support the protobuf/flatbuffer style of using native int types with annotations. However, integrating these annotations into the code generation system requires modifying the analyzer and type resolver. Could you provide me guidance on the best approach for this integration, or would you prefer to handle the code generation part since it touches core code?

@chaokunyang
Copy link
Collaborator

Hey @chaokunyang I've added the annotation definitions (@Uint8Type, @Uint16Type, @Uint16Type) to support the protobuf/flatbuffer style of using native int types with annotations. However, integrating these annotations into the code generation system requires modifying the analyzer and type resolver. Could you provide me guidance on the best approach for this integration, or would you prefer to handle the code generation part since it touches core code?

You may need to dive into the code, I don't write dart.

Copy link
Collaborator

@chaokunyang chaokunyang left a 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

@chaokunyang chaokunyang merged commit 7a19a06 into apache:main Jan 17, 2026
53 checks passed
@ayush00git
Copy link
Contributor Author

LGTM, please add unsgined number support to struct serializer in a following-up PR

sure, i'll just start working on this one..

chaokunyang pushed a commit that referenced this pull request Jan 19, 2026
…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..
@ayush00git ayush00git deleted the feat/uInt-Dart branch January 21, 2026 05:36
chaokunyang added a commit that referenced this pull request Jan 22, 2026
…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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Dart] support unsigned number for Dart

2 participants