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 7a19a069b feat: add unsigned number for dart (#3144)
7a19a069b is described below
commit 7a19a069b59e9e423f8ee2b790a644ea51656932
Author: Ayush Kumar <[email protected]>
AuthorDate: Sat Jan 17 19:22:56 2026 +0530
feat: add unsigned number for dart (#3144)
## 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`:
```dart
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:**
```dart
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:
```dart
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:
```dart
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:**
```bash
cd dart/packages/fory-test
dart test test/datatype_test/uint_test.dart
```
## Related Issue
- Closes #3131
## Does this PR introduce any user-facing change?
* [x] 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
* [x] 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.
---
.../fory-test/test/datatype_test/uint_test.dart | 232 ++++++++++++++++++++
dart/packages/fory/lib/fory.dart | 4 +
.../fory/lib/src/annotation/uint_types.dart | 105 +++++++++
dart/packages/fory/lib/src/const/obj_type.dart | 39 +++-
dart/packages/fory/lib/src/datatype/uint16.dart | 117 +++++++++++
dart/packages/fory/lib/src/datatype/uint32.dart | 117 +++++++++++
dart/packages/fory/lib/src/datatype/uint8.dart | 117 +++++++++++
.../src/serializer/primitive_type_serializer.dart | 234 +++++++++++++++++++++
8 files changed, 956 insertions(+), 9 deletions(-)
diff --git a/dart/packages/fory-test/test/datatype_test/uint_test.dart
b/dart/packages/fory-test/test/datatype_test/uint_test.dart
new file mode 100644
index 000000000..3866b3ec4
--- /dev/null
+++ b/dart/packages/fory-test/test/datatype_test/uint_test.dart
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+library;
+
+import 'package:fory/src/datatype/uint8.dart';
+import 'package:fory/src/datatype/uint16.dart';
+import 'package:fory/src/datatype/uint32.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('UInt8 behaviors', () {
+ group('range & conversion', () {
+ test('preserves in-range values', () {
+ var a = UInt8(0);
+ var b = UInt8(255);
+
+ expect(a.value, 0);
+ expect(b.value, 255);
+ });
+
+ test('wraps on overflow', () {
+ var a = UInt8(256); // Overflow to 0
+ var b = UInt8(257); // Overflow to 1
+ var c = UInt8(512); // Overflow to 0
+ var d = UInt8(-1); // Wraps to 255
+
+ expect(a.value, 0);
+ expect(b.value, 1);
+ expect(c.value, 0);
+ expect(d.value, 255);
+ });
+
+ test('truncates float inputs', () {
+ var a = UInt8(42.7);
+ var b = UInt8(255.9);
+
+ expect(a.value, 42);
+ expect(b.value, 255);
+ });
+
+ test('min/max constants and instances', () {
+ expect(UInt8.MIN_VALUE, 0);
+ expect(UInt8.MAX_VALUE, 255);
+ expect(UInt8.minValue.value, 0);
+ expect(UInt8.maxValue.value, 255);
+ });
+ });
+
+ group('arithmetic operators with overflow', () {
+ test('addition with overflow', () {
+ var a = UInt8(200);
+ var b = UInt8(100);
+ var result = a + b;
+
+ expect(result, isA<UInt8>());
+ expect(result.value, 44); // 200 + 100 = 300, which overflows to 44
+ });
+
+ test('subtraction with underflow', () {
+ var a = UInt8(50);
+ var b = UInt8(100);
+ var result = a - b;
+
+ expect(result, isA<UInt8>());
+ expect(result.value, 206); // 50 - 100 = -50, which wraps to 206
+ });
+
+ test('multiplication with overflow', () {
+ var a = UInt8(20);
+ var b = UInt8(20);
+ var result = a * b;
+
+ expect(result, isA<UInt8>());
+ expect(result.value, 144); // 20 * 20 = 400, which overflows to 144
+ });
+ });
+
+ group('comparison operators', () {
+ test('less than', () {
+ var a = UInt8(10);
+ var b = UInt8(20);
+
+ expect(a < b, isTrue);
+ expect(b < a, isFalse);
+ expect(a < 20, isTrue);
+ });
+
+ test('greater than', () {
+ var a = UInt8(200);
+ var b = UInt8(100);
+
+ expect(a > b, isTrue);
+ expect(b > a, isFalse);
+ expect(a > 100, isTrue);
+ });
+ });
+
+ group('equality & hashCode', () {
+ test('equality', () {
+ var a = UInt8(42);
+ var b = UInt8(42);
+ var c = UInt8(43);
+
+ expect(a == b, isTrue);
+ expect(a == c, isFalse);
+ expect(a == 42, isTrue);
+ });
+
+ test('hashCode consistency', () {
+ var a = UInt8(42);
+ var b = UInt8(42);
+
+ expect(a.hashCode == b.hashCode, isTrue);
+ });
+ });
+ });
+
+ group('UInt16 behaviors', () {
+ group('range & conversion', () {
+ test('preserves in-range values', () {
+ var a = UInt16(0);
+ var b = UInt16(65535);
+
+ expect(a.value, 0);
+ expect(b.value, 65535);
+ });
+
+ test('wraps on overflow', () {
+ var a = UInt16(65536); // Overflow to 0
+ var b = UInt16(65537); // Overflow to 1
+ var c = UInt16(-1); // Wraps to 65535
+
+ expect(a.value, 0);
+ expect(b.value, 1);
+ expect(c.value, 65535);
+ });
+
+ test('min/max constants', () {
+ expect(UInt16.MIN_VALUE, 0);
+ expect(UInt16.MAX_VALUE, 65535);
+ expect(UInt16.minValue.value, 0);
+ expect(UInt16.maxValue.value, 65535);
+ });
+ });
+
+ group('arithmetic operators', () {
+ test('addition with overflow', () {
+ var a = UInt16(60000);
+ var b = UInt16(10000);
+ var result = a + b;
+
+ expect(result, isA<UInt16>());
+ expect(result.value, 4464); // 70000 overflows to 4464
+ });
+
+ test('subtraction with underflow', () {
+ var a = UInt16(1000);
+ var b = UInt16(2000);
+ var result = a - b;
+
+ expect(result, isA<UInt16>());
+ expect(result.value, 64536); // -1000 wraps to 64536
+ });
+ });
+ });
+
+ group('UInt32 behaviors', () {
+ group('range & conversion', () {
+ test('preserves in-range values', () {
+ var a = UInt32(0);
+ var b = UInt32(4294967295);
+
+ expect(a.value, 0);
+ expect(b.value, 4294967295);
+ });
+
+ test('wraps on overflow', () {
+ var a = UInt32(4294967296); // Overflow to 0
+ var b = UInt32(4294967297); // Overflow to 1
+ var c = UInt32(-1); // Wraps to 4294967295
+
+ expect(a.value, 0);
+ expect(b.value, 1);
+ expect(c.value, 4294967295);
+ });
+
+ test('min/max constants', () {
+ expect(UInt32.MIN_VALUE, 0);
+ expect(UInt32.MAX_VALUE, 4294967295);
+ expect(UInt32.minValue.value, 0);
+ expect(UInt32.maxValue.value, 4294967295);
+ });
+ });
+
+ group('arithmetic operators', () {
+ test('addition with overflow', () {
+ var a = UInt32(4000000000);
+ var b = UInt32(500000000);
+ var result = a + b;
+
+ expect(result, isA<UInt32>());
+ expect(result.value, 205032704); // 4500000000 % (2^32) = 205032704
+ });
+
+ test('large value operations', () {
+ var a = UInt32(2147483648); // Half of max value
+ var b = UInt32(2147483648);
+ var result = a + b;
+
+ expect(result, isA<UInt32>());
+ expect(result.value, 0); // Exactly overflows to 0
+ });
+ });
+ });
+}
diff --git a/dart/packages/fory/lib/fory.dart b/dart/packages/fory/lib/fory.dart
index 023277f3f..a6493e9ad 100644
--- a/dart/packages/fory/lib/fory.dart
+++ b/dart/packages/fory/lib/fory.dart
@@ -26,6 +26,7 @@ export 'src/annotation/fory_key.dart';
export 'src/annotation/fory_class.dart';
export 'src/annotation/fory_enum.dart';
export 'src/annotation/fory_constructor.dart';
+export 'src/annotation/uint_types.dart';
// Analysis meta
export 'src/meta/specs/class_spec.dart';
@@ -56,6 +57,9 @@ export 'src/datatype/fory_fixed_num.dart';
export 'src/datatype/int8.dart';
export 'src/datatype/int16.dart';
export 'src/datatype/int32.dart';
+export 'src/datatype/uint8.dart';
+export 'src/datatype/uint16.dart';
+export 'src/datatype/uint32.dart';
export 'src/datatype/float32.dart';
export 'src/datatype/local_date.dart';
export 'src/datatype/timestamp.dart';
diff --git a/dart/packages/fory/lib/src/annotation/uint_types.dart
b/dart/packages/fory/lib/src/annotation/uint_types.dart
new file mode 100644
index 000000000..a2b0af97a
--- /dev/null
+++ b/dart/packages/fory/lib/src/annotation/uint_types.dart
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+library;
+
+/// Annotation to mark a field as unsigned 8-bit integer (0-255).
+///
+/// Use this annotation on `int` fields to serialize them as UINT8 type.
+///
+/// Example:
+/// ```dart
+/// class MyStruct {
+/// @Uint8Type()
+/// int age; // Serialized as UINT8
+/// }
+/// ```
+class Uint8Type {
+ const Uint8Type();
+}
+
+/// Annotation to mark a field as unsigned 16-bit integer (0-65535).
+///
+/// Use this annotation on `int` fields to serialize them as UINT16 type.
+///
+/// Example:
+/// ```dart
+/// class MyStruct {
+/// @Uint16Type()
+/// int port; // Serialized as UINT16
+/// }
+/// ```
+class Uint16Type {
+ const Uint16Type();
+}
+
+/// Encoding options for 32-bit and 64-bit unsigned integers.
+enum UintEncoding {
+ /// Fixed-length encoding (4 bytes for uint32, 8 bytes for uint64)
+ fixed,
+
+ /// Variable-length encoding (VAR_UINT32 or VAR_UINT64)
+ varint,
+
+ /// Tagged variable-length encoding (only for uint64)
+ tagged,
+}
+
+/// Annotation to mark a field as unsigned 32-bit integer (0-4294967295).
+///
+/// Use this annotation on `int` fields to serialize them as UINT32 or
VAR_UINT32 type.
+///
+/// Example:
+/// ```dart
+/// class MyStruct {
+/// @Uint32Type()
+/// int count; // Serialized as UINT32 (fixed)
+///
+/// @Uint32Type(encoding: UintEncoding.varint)
+/// int varCount; // Serialized as VAR_UINT32
+/// }
+/// ```
+class Uint32Type {
+ final UintEncoding encoding;
+
+ const Uint32Type({this.encoding = UintEncoding.fixed});
+}
+
+/// Annotation to mark a field as unsigned 64-bit integer.
+///
+/// Use this annotation on `int` fields to serialize them as UINT64,
VAR_UINT64, or TAGGED_UINT64 type.
+///
+/// Example:
+/// ```dart
+/// class MyStruct {
+/// @Uint64Type()
+/// int id; // Serialized as UINT64 (fixed)
+///
+/// @Uint64Type(encoding: UintEncoding.varint)
+/// int varId; // Serialized as VAR_UINT64
+///
+/// @Uint64Type(encoding: UintEncoding.tagged)
+/// int taggedId; // Serialized as TAGGED_UINT64
+/// }
+/// ```
+class Uint64Type {
+ final UintEncoding encoding;
+
+ const Uint64Type({this.encoding = UintEncoding.fixed});
+}
diff --git a/dart/packages/fory/lib/src/const/obj_type.dart
b/dart/packages/fory/lib/src/const/obj_type.dart
index da0c3d3f3..a7fb48df6 100644
--- a/dart/packages/fory/lib/src/const/obj_type.dart
+++ b/dart/packages/fory/lib/src/const/obj_type.dart
@@ -148,28 +148,49 @@ enum ObjType {
INT8_ARRAY(31, true), // 31
/// One dimensional int16 array.
- INT16_ARRAY(32, true),
+ INT16_ARRAY(32, true), // 32
/// One dimensional int32 array.
- INT32_ARRAY(33, true),
+ INT32_ARRAY(33, true), // 33
/// One dimensional int64 array.
- INT64_ARRAY(34, true),
+ INT64_ARRAY(34, true), // 34
/// One dimensional half_float_16 array.
- FLOAT16_ARRAY(35, true),
+ FLOAT16_ARRAY(35, true), // 35
/// One dimensional float32 array.
- FLOAT32_ARRAY(36, true),
+ FLOAT32_ARRAY(36, true), // 36
/// One dimensional float64 array.
- FLOAT64_ARRAY(37, true),
+ FLOAT64_ARRAY(37, true), // 37
/// An (arrow record batch) object.
- ARROW_RECORD_BATCH(38, false),
+ ARROW_RECORD_BATCH(38, false), // 38
/// An (arrow table) object.
- ARROW_TABLE(39, false);
+ ARROW_TABLE(39, false), // 39
+
+ /// Unsigned 8-bit integer
+ UINT8(40, true), // 40
+
+ /// Unsigned 16-bit little-endian integer
+ UINT16(41, true), // 41
+
+ /// Unsigned 32-bit little-endian integer
+ UINT32(42, true), // 42
+
+ /// var_uint32: a 32-bit unsigned integer which uses fory var_uint32
encoding.
+ VAR_UINT32(43, true), // 43
+
+ /// Unsigned 64-bit little-endian integer
+ UINT64(44, true), // 44
+
+ /// var_uint64: a 64-bit unsigned integer which uses fory var_uint64
encoding.
+ VAR_UINT64(45, true), // 45
+
+ /// tagged_uint64: a 64-bit unsigned integer which uses fory tagged
var_uint64 encoding.
+ TAGGED_UINT64(46, true); // 46
final int id;
final bool independent;
@@ -178,7 +199,7 @@ enum ObjType {
static ObjType? fromId(int id) {
// The current implementation is linear, so it's simpler here. If the id
and ordinal become irregular in the future, this won't work.
- if (id >= 1 && id <= 39) return ObjType.values[id];
+ if (id >= 1 && id <= 46) return ObjType.values[id];
return null;
}
diff --git a/dart/packages/fory/lib/src/datatype/uint16.dart
b/dart/packages/fory/lib/src/datatype/uint16.dart
new file mode 100644
index 000000000..e8aa7be69
--- /dev/null
+++ b/dart/packages/fory/lib/src/datatype/uint16.dart
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+import 'fory_fixed_num.dart';
+
+/// UInt16: 16-bit unsigned integer (0 to 65535)
+final class UInt16 extends FixedNum {
+ static const int MIN_VALUE = 0;
+ static const int MAX_VALUE = 65535;
+
+ static UInt16 get maxValue => UInt16(MAX_VALUE);
+ static UInt16 get minValue => UInt16(MIN_VALUE);
+
+ final int _value;
+
+ UInt16(num input) : _value = _convert(input);
+
+ static int _convert(num value) {
+ if (value is int) {
+ // Apply 16-bit unsigned integer overflow behavior
+ return value & 0xFFFF; // Keep only the lowest 16 bits (0-65535)
+ } else {
+ return _convert(value.toInt());
+ }
+ }
+
+ @override
+ int get value => _value;
+
+ // Operators
+ UInt16 operator +(dynamic other) =>
+ UInt16(_value + (other is FixedNum ? other.value : other));
+
+ UInt16 operator -(dynamic other) =>
+ UInt16(_value - (other is FixedNum ? other.value : other));
+
+ UInt16 operator *(dynamic other) =>
+ UInt16(_value * (other is FixedNum ? other.value : other));
+
+ double operator /(dynamic other) =>
+ _value / (other is FixedNum ? other.value : other);
+
+ UInt16 operator ~/(dynamic other) =>
+ UInt16(_value ~/ (other is FixedNum ? other.value : other));
+
+ UInt16 operator %(dynamic other) =>
+ UInt16(_value % (other is FixedNum ? other.value : other));
+
+ UInt16 operator -() => UInt16(-_value);
+
+ // Bitwise operations
+ UInt16 operator &(dynamic other) =>
+ UInt16(_value & (other is FixedNum ? other.value : other).toInt());
+
+ UInt16 operator |(dynamic other) =>
+ UInt16(_value | (other is FixedNum ? other.value : other).toInt());
+
+ UInt16 operator ^(dynamic other) =>
+ UInt16(_value ^ (other is FixedNum ? other.value : other).toInt());
+
+ UInt16 operator ~() => UInt16(~_value);
+
+ UInt16 operator <<(int shiftAmount) => UInt16(_value << shiftAmount);
+ UInt16 operator >>(int shiftAmount) => UInt16(_value >> shiftAmount);
+
+ // Comparison
+ bool operator <(dynamic other) =>
+ _value < (other is FixedNum ? other.value : other);
+
+ bool operator <=(dynamic other) =>
+ _value <= (other is FixedNum ? other.value : other);
+
+ bool operator >(dynamic other) =>
+ _value > (other is FixedNum ? other.value : other);
+
+ bool operator >=(dynamic other) =>
+ _value >= (other is FixedNum ? other.value : other);
+
+ // Equality
+ @override
+ bool operator ==(Object other) {
+ if (other is FixedNum) return _value == other.value;
+ if (other is num) return _value == other;
+ return false;
+ }
+
+ @override
+ int get hashCode => _value.hashCode;
+
+ // Common num methods
+ int abs() => _value;
+ int get sign => _value == 0 ? 0 : 1;
+ bool get isNegative => false;
+
+ // Type conversions
+ int toInt() => _value;
+ double toDouble() => _value.toDouble();
+
+ @override
+ String toString() => _value.toString();
+}
diff --git a/dart/packages/fory/lib/src/datatype/uint32.dart
b/dart/packages/fory/lib/src/datatype/uint32.dart
new file mode 100644
index 000000000..f913cd7af
--- /dev/null
+++ b/dart/packages/fory/lib/src/datatype/uint32.dart
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+import 'fory_fixed_num.dart';
+
+/// UInt32: 32-bit unsigned integer (0 to 4294967295)
+final class UInt32 extends FixedNum {
+ static const int MIN_VALUE = 0;
+ static const int MAX_VALUE = 4294967295;
+
+ static UInt32 get maxValue => UInt32(MAX_VALUE);
+ static UInt32 get minValue => UInt32(MIN_VALUE);
+
+ final int _value;
+
+ UInt32(num input) : _value = _convert(input);
+
+ static int _convert(num value) {
+ if (value is int) {
+ // Apply 32-bit unsigned integer overflow behavior
+ return value & 0xFFFFFFFF; // Keep only the lowest 32 bits
(0-4294967295)
+ } else {
+ return _convert(value.toInt());
+ }
+ }
+
+ @override
+ int get value => _value;
+
+ // Operators
+ UInt32 operator +(dynamic other) =>
+ UInt32(_value + (other is FixedNum ? other.value : other));
+
+ UInt32 operator -(dynamic other) =>
+ UInt32(_value - (other is FixedNum ? other.value : other));
+
+ UInt32 operator *(dynamic other) =>
+ UInt32(_value * (other is FixedNum ? other.value : other));
+
+ double operator /(dynamic other) =>
+ _value / (other is FixedNum ? other.value : other);
+
+ UInt32 operator ~/(dynamic other) =>
+ UInt32(_value ~/ (other is FixedNum ? other.value : other));
+
+ UInt32 operator %(dynamic other) =>
+ UInt32(_value % (other is FixedNum ? other.value : other));
+
+ UInt32 operator -() => UInt32(-_value);
+
+ // Bitwise operations
+ UInt32 operator &(dynamic other) =>
+ UInt32(_value & (other is FixedNum ? other.value : other).toInt());
+
+ UInt32 operator |(dynamic other) =>
+ UInt32(_value | (other is FixedNum ? other.value : other).toInt());
+
+ UInt32 operator ^(dynamic other) =>
+ UInt32(_value ^ (other is FixedNum ? other.value : other).toInt());
+
+ UInt32 operator ~() => UInt32(~_value);
+
+ UInt32 operator <<(int shiftAmount) => UInt32(_value << shiftAmount);
+ UInt32 operator >>(int shiftAmount) => UInt32(_value >> shiftAmount);
+
+ // Comparison
+ bool operator <(dynamic other) =>
+ _value < (other is FixedNum ? other.value : other);
+
+ bool operator <=(dynamic other) =>
+ _value <= (other is FixedNum ? other.value : other);
+
+ bool operator >(dynamic other) =>
+ _value > (other is FixedNum ? other.value : other);
+
+ bool operator >=(dynamic other) =>
+ _value >= (other is FixedNum ? other.value : other);
+
+ // Equality
+ @override
+ bool operator ==(Object other) {
+ if (other is FixedNum) return _value == other.value;
+ if (other is num) return _value == other;
+ return false;
+ }
+
+ @override
+ int get hashCode => _value.hashCode;
+
+ // Common num methods
+ int abs() => _value;
+ int get sign => _value == 0 ? 0 : 1;
+ bool get isNegative => false;
+
+ // Type conversions
+ int toInt() => _value;
+ double toDouble() => _value.toDouble();
+
+ @override
+ String toString() => _value.toString();
+}
diff --git a/dart/packages/fory/lib/src/datatype/uint8.dart
b/dart/packages/fory/lib/src/datatype/uint8.dart
new file mode 100644
index 000000000..be14b8773
--- /dev/null
+++ b/dart/packages/fory/lib/src/datatype/uint8.dart
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+import 'fory_fixed_num.dart';
+
+/// UInt8: 8-bit unsigned integer (0 to 255)
+final class UInt8 extends FixedNum {
+ static const int MIN_VALUE = 0;
+ static const int MAX_VALUE = 255;
+
+ static UInt8 get maxValue => UInt8(MAX_VALUE);
+ static UInt8 get minValue => UInt8(MIN_VALUE);
+
+ final int _value;
+
+ UInt8(num input) : _value = _convert(input);
+
+ static int _convert(num value) {
+ if (value is int) {
+ // Apply 8-bit unsigned integer overflow behavior
+ return value & 0xFF; // Keep only the lowest 8 bits (0-255)
+ } else {
+ return _convert(value.toInt());
+ }
+ }
+
+ @override
+ int get value => _value;
+
+ // Operators
+ UInt8 operator +(dynamic other) =>
+ UInt8(_value + (other is FixedNum ? other.value : other));
+
+ UInt8 operator -(dynamic other) =>
+ UInt8(_value - (other is FixedNum ? other.value : other));
+
+ UInt8 operator *(dynamic other) =>
+ UInt8(_value * (other is FixedNum ? other.value : other));
+
+ double operator /(dynamic other) =>
+ _value / (other is FixedNum ? other.value : other);
+
+ UInt8 operator ~/(dynamic other) =>
+ UInt8(_value ~/ (other is FixedNum ? other.value : other));
+
+ UInt8 operator %(dynamic other) =>
+ UInt8(_value % (other is FixedNum ? other.value : other));
+
+ UInt8 operator -() => UInt8(-_value);
+
+ // Bitwise operations
+ UInt8 operator &(dynamic other) =>
+ UInt8(_value & (other is FixedNum ? other.value : other).toInt());
+
+ UInt8 operator |(dynamic other) =>
+ UInt8(_value | (other is FixedNum ? other.value : other).toInt());
+
+ UInt8 operator ^(dynamic other) =>
+ UInt8(_value ^ (other is FixedNum ? other.value : other).toInt());
+
+ UInt8 operator ~() => UInt8(~_value);
+
+ UInt8 operator <<(int shiftAmount) => UInt8(_value << shiftAmount);
+ UInt8 operator >>(int shiftAmount) => UInt8(_value >> shiftAmount);
+
+ // Comparison
+ bool operator <(dynamic other) =>
+ _value < (other is FixedNum ? other.value : other);
+
+ bool operator <=(dynamic other) =>
+ _value <= (other is FixedNum ? other.value : other);
+
+ bool operator >(dynamic other) =>
+ _value > (other is FixedNum ? other.value : other);
+
+ bool operator >=(dynamic other) =>
+ _value >= (other is FixedNum ? other.value : other);
+
+ // Equality
+ @override
+ bool operator ==(Object other) {
+ if (other is FixedNum) return _value == other.value;
+ if (other is num) return _value == other;
+ return false;
+ }
+
+ @override
+ int get hashCode => _value.hashCode;
+
+ // Common num methods
+ int abs() => _value;
+ int get sign => _value == 0 ? 0 : 1;
+ bool get isNegative => false;
+
+ // Type conversions
+ int toInt() => _value;
+ double toDouble() => _value.toDouble();
+
+ @override
+ String toString() => _value.toString();
+}
diff --git
a/dart/packages/fory/lib/src/serializer/primitive_type_serializer.dart
b/dart/packages/fory/lib/src/serializer/primitive_type_serializer.dart
index 1d6ff45b7..40a13871d 100644
--- a/dart/packages/fory/lib/src/serializer/primitive_type_serializer.dart
+++ b/dart/packages/fory/lib/src/serializer/primitive_type_serializer.dart
@@ -24,6 +24,9 @@ import 'package:fory/src/datatype/fory_fixed_num.dart';
import 'package:fory/src/datatype/int16.dart';
import 'package:fory/src/datatype/int32.dart';
import 'package:fory/src/datatype/int8.dart';
+import 'package:fory/src/datatype/uint8.dart';
+import 'package:fory/src/datatype/uint16.dart';
+import 'package:fory/src/datatype/uint32.dart';
import 'package:fory/src/deserializer_pack.dart';
import 'package:fory/src/memory/byte_reader.dart';
import 'package:fory/src/memory/byte_writer.dart';
@@ -305,3 +308,234 @@ final class Float64Serializer extends Serializer<double>{
bw.writeFloat64(v);
}
}
+
+final class _UInt8SerializerCache extends PrimitiveSerializerCache{
+ static UInt8Serializer? serRef;
+ static UInt8Serializer? serNoRef;
+
+ const _UInt8SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= UInt8Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= UInt8Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class UInt8Serializer extends Serializer<FixedNum>{
+ static const SerializerCache cache = _UInt8SerializerCache();
+ UInt8Serializer._(bool writeRef): super(ObjType.UINT8, writeRef);
+
+ @override
+ UInt8 read(ByteReader br, int refId, DeserializerPack pack) {
+ return UInt8(br.readUint8());
+ }
+
+ @override
+ void write(ByteWriter bw, covariant UInt8 v, SerializerPack pack) {
+ bw.writeUint8(v.toInt());
+ }
+}
+
+final class _UInt16SerializerCache extends PrimitiveSerializerCache{
+ static UInt16Serializer? serRef;
+ static UInt16Serializer? serNoRef;
+
+ const _UInt16SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= UInt16Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= UInt16Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class UInt16Serializer extends Serializer<FixedNum>{
+ static const SerializerCache cache = _UInt16SerializerCache();
+ UInt16Serializer._(bool writeRef): super(ObjType.UINT16, writeRef);
+
+ @override
+ UInt16 read(ByteReader br, int refId, DeserializerPack pack) {
+ return UInt16(br.readUint16());
+ }
+
+ @override
+ void write(ByteWriter bw, covariant UInt16 v, SerializerPack pack) {
+ bw.writeUint16(v.toInt());
+ }
+}
+
+final class _UInt32SerializerCache extends PrimitiveSerializerCache{
+ static UInt32Serializer? serRef;
+ static UInt32Serializer? serNoRef;
+
+ const _UInt32SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= UInt32Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= UInt32Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class UInt32Serializer extends Serializer<FixedNum>{
+ static const SerializerCache cache = _UInt32SerializerCache();
+ UInt32Serializer._(bool writeRef): super(ObjType.UINT32, writeRef);
+
+ @override
+ UInt32 read(ByteReader br, int refId, DeserializerPack pack) {
+ return UInt32(br.readUint32());
+ }
+
+ @override
+ void write(ByteWriter bw, covariant UInt32 v, SerializerPack pack) {
+ bw.writeUint32(v.toInt());
+ }
+}
+
+final class _VarUInt32SerializerCache extends PrimitiveSerializerCache{
+ static VarUInt32Serializer? serRef;
+ static VarUInt32Serializer? serNoRef;
+
+ const _VarUInt32SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= VarUInt32Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= VarUInt32Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class VarUInt32Serializer extends Serializer<FixedNum>{
+ static const SerializerCache cache = _VarUInt32SerializerCache();
+ VarUInt32Serializer._(bool writeRef): super(ObjType.VAR_UINT32, writeRef);
+
+ @override
+ UInt32 read(ByteReader br, int refId, DeserializerPack pack) {
+ return UInt32(br.readVarUint32());
+ }
+
+ @override
+ void write(ByteWriter bw, covariant UInt32 v, SerializerPack pack) {
+ bw.writeVarUint32(v.toInt());
+ }
+}
+
+final class _UInt64SerializerCache extends PrimitiveSerializerCache{
+ static UInt64Serializer? serRef;
+ static UInt64Serializer? serNoRef;
+
+ const _UInt64SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= UInt64Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= UInt64Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class UInt64Serializer extends Serializer<int> {
+ static const SerializerCache cache = _UInt64SerializerCache();
+ UInt64Serializer._(bool writeRef): super(ObjType.UINT64, writeRef);
+
+ @override
+ int read(ByteReader br, int refId, DeserializerPack pack) {
+ return br.readUint64();
+ }
+
+ @override
+ void write(ByteWriter bw, int v, SerializerPack pack) {
+ bw.writeUint64(v);
+ }
+}
+
+final class _VarUInt64SerializerCache extends PrimitiveSerializerCache{
+ static VarUInt64Serializer? serRef;
+ static VarUInt64Serializer? serNoRef;
+
+ const _VarUInt64SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= VarUInt64Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= VarUInt64Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class VarUInt64Serializer extends Serializer<int> {
+ static const SerializerCache cache = _VarUInt64SerializerCache();
+ VarUInt64Serializer._(bool writeRef): super(ObjType.VAR_UINT64, writeRef);
+
+ @override
+ int read(ByteReader br, int refId, DeserializerPack pack) {
+ return br.readVarInt64();
+ }
+
+ @override
+ void write(ByteWriter bw, int v, SerializerPack pack) {
+ bw.writeVarInt64(v);
+ }
+}
+
+final class _TaggedUInt64SerializerCache extends PrimitiveSerializerCache{
+ static TaggedUInt64Serializer? serRef;
+ static TaggedUInt64Serializer? serNoRef;
+
+ const _TaggedUInt64SerializerCache();
+
+ @override
+ Serializer getSerWithRef(bool writeRef) {
+ if (writeRef){
+ serRef ??= TaggedUInt64Serializer._(true);
+ return serRef!;
+ } else {
+ serNoRef ??= TaggedUInt64Serializer._(false);
+ return serNoRef!;
+ }
+ }
+}
+
+final class TaggedUInt64Serializer extends Serializer<int> {
+ static const SerializerCache cache = _TaggedUInt64SerializerCache();
+ TaggedUInt64Serializer._(bool writeRef): super(ObjType.TAGGED_UINT64,
writeRef);
+
+ @override
+ int read(ByteReader br, int refId, DeserializerPack pack) {
+ return br.readVarInt64();
+ }
+
+ @override
+ void write(ByteWriter bw, int v, SerializerPack pack) {
+ bw.writeVarInt64(v);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]