From a75836bc0edc5aa676434aad3b8f4bd1198f27c4 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 09:10:22 +0800 Subject: [PATCH 01/14] feat(dart): support int64 and uint64 on web --- .../fory/example/manual_serializer.dart | 4 +- dart/packages/fory/lib/fory.dart | 1 + dart/packages/fory/lib/src/buffer.dart | 143 ++---- .../fory/lib/src/codegen/fory_generator.dart | 98 +++- .../lib/src/codegen/generated_support.dart | 164 ++---- .../lib/src/context/meta_string_reader.dart | 7 +- .../fory/lib/src/context/read_context.dart | 21 +- .../fory/lib/src/context/write_context.dart | 12 +- .../fory/lib/src/meta/field_type.dart | 3 + .../fory/lib/src/meta/meta_string.dart | 3 +- dart/packages/fory/lib/src/meta/type_def.dart | 3 +- .../packages/fory/lib/src/meta/type_meta.dart | 9 +- .../fory/lib/src/resolver/type_resolver.dart | 47 +- .../serializer/collection_serializers.dart | 6 +- .../src/serializer/primitive_serializers.dart | 28 +- .../src/serializer/scalar_serializers.dart | 17 +- .../src/serializer/serializer_support.dart | 76 ++- .../lib/src/serializer/time_serializers.dart | 34 +- .../serializer/typed_array_serializers.dart | 79 +-- .../packages/fory/lib/src/types/bfloat16.dart | 36 +- dart/packages/fory/lib/src/types/float16.dart | 34 +- dart/packages/fory/lib/src/types/int64.dart | 21 + .../fory/lib/src/types/int64_native.dart | 261 ++++++++++ .../fory/lib/src/types/int64_web.dart | 468 ++++++++++++++++++ .../fory/lib/src/types/timestamp.dart | 12 +- dart/packages/fory/lib/src/types/uint64.dart | 130 +---- .../fory/lib/src/types/uint64_native.dart | 243 +++++++++ .../fory/lib/src/types/uint64_web.dart | 430 ++++++++++++++++ .../packages/fory/lib/src/util/hash_util.dart | 9 +- .../fory/lib/src/util/int64_codec.dart | 84 ++++ dart/packages/fory/test/buffer_test.dart | 246 +++++---- .../codegen_conversion_expression_test.dart | 3 + .../fory/test/enum_union_serializer_test.dart | 9 +- .../fory/test/manual_registration_test.dart | 6 +- .../fory/test/numeric_wrapper_test.dart | 31 +- ...calar_and_typed_array_serializer_test.dart | 11 +- .../fory/test/time_serializer_test.dart | 28 +- .../fory/test/unsigned_serializer_test.dart | 22 +- 38 files changed, 2189 insertions(+), 650 deletions(-) create mode 100644 dart/packages/fory/lib/src/types/int64.dart create mode 100644 dart/packages/fory/lib/src/types/int64_native.dart create mode 100644 dart/packages/fory/lib/src/types/int64_web.dart create mode 100644 dart/packages/fory/lib/src/types/uint64_native.dart create mode 100644 dart/packages/fory/lib/src/types/uint64_web.dart create mode 100644 dart/packages/fory/lib/src/util/int64_codec.dart diff --git a/dart/packages/fory/example/manual_serializer.dart b/dart/packages/fory/example/manual_serializer.dart index fd016882d9..d269c3128e 100644 --- a/dart/packages/fory/example/manual_serializer.dart +++ b/dart/packages/fory/example/manual_serializer.dart @@ -23,7 +23,7 @@ final class Person { Person(this.name, this.age); final String name; - final int age; + final Int64 age; } final class PersonSerializer extends Serializer { @@ -52,7 +52,7 @@ void main() { typeName: 'Person', ); - final person = Person('Ada', 36); + final person = Person('Ada', Int64(36)); final bytes = fory.serialize(person); final roundTrip = fory.deserialize(bytes); print(roundTrip.name); diff --git a/dart/packages/fory/lib/fory.dart b/dart/packages/fory/lib/fory.dart index 86232c609a..67fac76e45 100644 --- a/dart/packages/fory/lib/fory.dart +++ b/dart/packages/fory/lib/fory.dart @@ -55,6 +55,7 @@ export 'src/types/float16.dart'; export 'src/types/float32.dart'; export 'src/types/int16.dart'; export 'src/types/int32.dart'; +export 'src/types/int64.dart'; export 'src/types/int8.dart'; export 'src/types/local_date.dart'; export 'src/types/timestamp.dart'; diff --git a/dart/packages/fory/lib/src/buffer.dart b/dart/packages/fory/lib/src/buffer.dart index ee1cdb49a7..4c5dbbaf84 100644 --- a/dart/packages/fory/lib/src/buffer.dart +++ b/dart/packages/fory/lib/src/buffer.dart @@ -24,13 +24,9 @@ import 'package:meta/meta.dart'; import 'package:fory/src/types/bfloat16.dart'; import 'package:fory/src/types/float16.dart'; - -final BigInt _mask64Big = (BigInt.one << 64) - BigInt.one; -final BigInt _sevenBitMaskBig = BigInt.from(0x7f); -final BigInt _byteMaskBig = BigInt.from(0xff); -const bool _useBigIntVarint64 = - bool.fromEnvironment('dart.library.js_interop') || - bool.fromEnvironment('dart.library.js_util'); +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; +import 'package:fory/src/util/int64_codec.dart'; /// A reusable byte buffer with explicit reader and writer indices. /// @@ -195,29 +191,29 @@ final class Buffer { } /// Writes a signed little-endian 64-bit integer. - void writeInt64(int value) { + void writeInt64(Int64 value) { ensureWritable(8); - _view.setInt64(_writerIndex, value, Endian.little); + writeInt64LittleEndian(_view, _writerIndex, value); _writerIndex += 8; } /// Reads a signed little-endian 64-bit integer. - int readInt64() { - final value = _view.getInt64(_readerIndex, Endian.little); + Int64 readInt64() { + final value = readInt64LittleEndian(_view, _readerIndex); _readerIndex += 8; return value; } /// Writes an unsigned little-endian 64-bit integer. - void writeUint64(int value) { + void writeUint64(Uint64 value) { ensureWritable(8); - _view.setUint64(_writerIndex, value, Endian.little); + writeUint64LittleEndian(_view, _writerIndex, value); _writerIndex += 8; } /// Reads an unsigned little-endian 64-bit integer. - int readUint64() { - final value = _view.getUint64(_readerIndex, Endian.little); + Uint64 readUint64() { + final value = readUint64LittleEndian(_view, _readerIndex); _readerIndex += 8; return value; } @@ -318,83 +314,42 @@ final class Buffer { } /// Writes a zig-zag encoded signed 32-bit varint. - void writeVarInt32(int value) => writeVarUint32((value << 1) ^ (value >> 31)); + void writeVarInt32(int value) => + writeVarUint32(((value << 1) ^ (value >> 31)).toUnsigned(32)); /// Reads a zig-zag encoded signed 32-bit varint. int readVarInt32() { final value = readVarUint32(); - return (value >>> 1) ^ -(value & 1); + return ((value >>> 1) ^ -(value & 1)).toSigned(32); } /// Writes an unsigned 64-bit varint. - void writeVarUint64(int value) { - if (!_useBigIntVarint64) { - var remaining = value; - for (var index = 0; index < 8; index += 1) { - final chunk = remaining & 0x7f; - remaining = remaining >>> 7; - if (remaining == 0) { - writeUint8(chunk); - return; - } - writeUint8(chunk | 0x80); - } - writeUint8(remaining & 0xff); - return; - } - _writeVarUint64BigInt(BigInt.from(value) & _mask64Big); + void writeVarUint64(Uint64 value) { + writeVarUint64Bytes(value, writeUint8); } /// Reads an unsigned 64-bit varint. - int readVarUint64() { - if (!_useBigIntVarint64) { - var shift = 0; - var result = 0; - while (shift < 56) { - final byte = readUint8(); - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | (readUint8() << 56); - } - return _readVarUint64BigInt().toInt(); + Uint64 readVarUint64() { + return readVarUint64Bytes(readUint8); } /// Writes a zig-zag encoded signed 64-bit varint. - void writeVarInt64(int value) { - if (!_useBigIntVarint64) { - writeVarUint64((value << 1) ^ (value >> 63)); - return; - } - final signed = BigInt.from(value); - final zigZag = ((signed << 1) ^ BigInt.from(value >> 63)) & _mask64Big; - _writeVarUint64BigInt(zigZag); + void writeVarInt64(Int64 value) { + writeVarUint64(zigZagEncodeInt64(value)); } /// Reads a zig-zag encoded signed 64-bit varint. - int readVarInt64() { - if (!_useBigIntVarint64) { - final encoded = readVarUint64(); - return (encoded >>> 1) ^ -(encoded & 1); - } - final encoded = _readVarUint64BigInt(); - final magnitude = (encoded >> 1).toInt(); - if ((encoded & BigInt.one) == BigInt.zero) { - return magnitude; - } - return -magnitude - 1; + Int64 readVarInt64() { + return zigZagDecodeInt64(readVarUint64()); } /// Writes a tagged signed 64-bit integer. /// /// Small values use four bytes. Larger values use a tag byte plus eight data /// bytes. - void writeTaggedInt64(int value) { + void writeTaggedInt64(Int64 value) { if (value >= -0x40000000 && value <= 0x3fffffff) { - writeInt32(value << 1); + writeInt32((value.toInt() << 1).toSigned(32)); return; } writeUint8(0x01); @@ -402,22 +357,22 @@ final class Buffer { } /// Reads a signed 64-bit integer written by [writeTaggedInt64]. - int readTaggedInt64() { + Int64 readTaggedInt64() { final readIndex = _readerIndex; final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _readerIndex = readIndex + 4; - return first >> 1; + return Int64(first.toSigned(32) ~/ 2); } - final value = _view.getInt64(readIndex + 1, Endian.little); + final value = readInt64LittleEndian(_view, readIndex + 1); _readerIndex = readIndex + 9; return value; } /// Writes a tagged unsigned 64-bit integer. - void writeTaggedUint64(int value) { + void writeTaggedUint64(Uint64 value) { if (value >= 0 && value <= 0x7fffffff) { - writeInt32(value << 1); + writeInt32(value.toInt() << 1); return; } writeUint8(0x01); @@ -425,14 +380,14 @@ final class Buffer { } /// Reads an unsigned 64-bit integer written by [writeTaggedUint64]. - int readTaggedUint64() { + Uint64 readTaggedUint64() { final readIndex = _readerIndex; final first = _view.getUint32(readIndex, Endian.little); if ((first & 1) == 0) { _readerIndex = readIndex + 4; - return first >>> 1; + return Uint64(first >>> 1); } - final value = _view.getUint64(readIndex + 1, Endian.little); + final value = readUint64LittleEndian(_view, readIndex + 1); _readerIndex = readIndex + 9; return value; } @@ -452,10 +407,10 @@ final class Buffer { int readVarUint32Small14() => readVarUint32(); /// Writes a small unsigned integer using the 64-bit varint path. - void writeVarUint36Small(int value) => writeVarUint64(value); + void writeVarUint36Small(int value) => writeVarUint64(Uint64(value)); /// Reads a small unsigned integer written by [writeVarUint36Small]. - int readVarUint36Small() => readVarUint64(); + int readVarUint36Small() => readVarUint64().toInt(); } @internal @@ -492,33 +447,3 @@ Uint8List bufferBytes(Buffer buffer) => buffer._bytes; @internal ByteData bufferByteData(Buffer buffer) => buffer._view; - -extension on Buffer { - void _writeVarUint64BigInt(BigInt value) { - var remaining = value & _mask64Big; - for (var index = 0; index < 8; index += 1) { - final chunk = (remaining & _sevenBitMaskBig).toInt(); - remaining >>= 7; - if (remaining == BigInt.zero) { - writeUint8(chunk); - return; - } - writeUint8(chunk | 0x80); - } - writeUint8((remaining & _byteMaskBig).toInt()); - } - - BigInt _readVarUint64BigInt() { - var shift = 0; - var result = BigInt.zero; - while (shift < 56) { - final byte = readUint8(); - result |= BigInt.from(byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | ((BigInt.from(readUint8()) & _byteMaskBig) << 56); - } -} diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index 3697cefd5b..7bee0cd5f0 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -33,10 +33,12 @@ class DebugGeneratedFieldTypeSpec { required this.nullable, required this.ref, required this.dynamic, + this.declaredTypeName, this.arguments = const [], }); final String typeLiteral; + final String? declaredTypeName; final int typeId; final bool nullable; final bool ref; @@ -218,6 +220,7 @@ final class ForyGenerator extends Generator { typeSpec is _ListTypeSpecInfo ? typeSpec.element : null; return _GeneratedFieldTypeSpec( typeLiteral: _typeReferenceLiteral(type), + declaredTypeName: _typeReferenceLiteral(type), typeId: _typeIdFor(type), nullable: nullable, ref: ref, @@ -239,6 +242,7 @@ final class ForyGenerator extends Generator { final valueSpec = typeSpec is _MapTypeSpecInfo ? typeSpec.value : null; return _GeneratedFieldTypeSpec( typeLiteral: _typeReferenceLiteral(type), + declaredTypeName: _typeReferenceLiteral(type), typeId: _typeIdFor(type), nullable: nullable, ref: ref, @@ -263,6 +267,7 @@ final class ForyGenerator extends Generator { } return _GeneratedFieldTypeSpec( typeLiteral: _typeReferenceLiteral(type), + declaredTypeName: _typeReferenceLiteral(type), typeId: _typeIdFor(type, integerAnnotation: integerAnnotation), nullable: nullable, ref: ref, @@ -885,6 +890,7 @@ final class ForyGenerator extends Generator { return ''' GeneratedFieldType( type: ${fieldType.typeLiteral}, + declaredTypeName: '${fieldType.typeLiteral}', typeId: ${fieldType.typeId}, nullable: ${fieldType.nullable}, ref: ${fieldType.ref}, @@ -912,6 +918,7 @@ GeneratedFieldType( ) { return _GeneratedFieldTypeSpec( typeLiteral: fieldType.typeLiteral, + declaredTypeName: fieldType.declaredTypeName, typeId: fieldType.typeId, nullable: fieldType.nullable, ref: fieldType.ref, @@ -1035,6 +1042,10 @@ GeneratedFieldType( case TypeIds.int32: case TypeIds.varInt32: return 'switch ($valueExpression) { int typed => typed, Int32 typed => typed.value, _ => throw StateError(\'Expected int or Int32.\') }'; + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + return 'switch ($valueExpression) { Int64 typed => typed.toInt(), int typed => typed, _ => throw StateError(\'Expected int or Int64.\') }'; case TypeIds.uint8: return 'switch ($valueExpression) { int typed => typed, Uint8 typed => typed.value, _ => throw StateError(\'Expected int or Uint8.\') }'; case TypeIds.uint16: @@ -1045,7 +1056,7 @@ GeneratedFieldType( case TypeIds.uint64: case TypeIds.varUint64: case TypeIds.taggedUint64: - return 'switch ($valueExpression) { int typed => typed, Uint64 typed => typed.value, _ => throw StateError(\'Expected int or Uint64.\') }'; + return 'switch ($valueExpression) { Uint64 typed => typed.toInt(), int typed => typed, _ => throw StateError(\'Expected int or Uint64.\') }'; default: return '$valueExpression as int'; } @@ -1333,7 +1344,7 @@ GeneratedFieldType( case TypeIds.float64: return '$cursorExpression.writeFloat64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.date: - return '$cursorExpression.writeVarInt64($valueExpression.toEpochDay())'; + return '$cursorExpression.writeVarInt64(Int64($valueExpression.toEpochDay()))'; case TypeIds.duration: return '$cursorExpression.writeVarInt64(generatedDurationWireSeconds($valueExpression)); $cursorExpression.writeInt32(generatedDurationWireNanoseconds($valueExpression))'; case TypeIds.timestamp: @@ -1370,11 +1381,17 @@ GeneratedFieldType( ? 'buffer.readVarInt32()' : 'Int32(buffer.readVarInt32())'; case TypeIds.int64: - return 'buffer.readInt64()'; + return field.type.isDartCoreInt + ? 'buffer.readInt64().toInt()' + : 'buffer.readInt64()'; case TypeIds.varInt64: - return 'buffer.readVarInt64()'; + return field.type.isDartCoreInt + ? 'buffer.readVarInt64().toInt()' + : 'buffer.readVarInt64()'; case TypeIds.taggedInt64: - return 'buffer.readTaggedInt64()'; + return field.type.isDartCoreInt + ? 'buffer.readTaggedInt64().toInt()' + : 'buffer.readTaggedInt64()'; case TypeIds.uint8: return field.type.isDartCoreInt ? 'buffer.readUint8()' @@ -1393,16 +1410,16 @@ GeneratedFieldType( : 'Uint32(buffer.readVarUint32())'; case TypeIds.uint64: return field.type.isDartCoreInt - ? 'buffer.readUint64()' - : 'Uint64(buffer.readUint64())'; + ? 'buffer.readUint64().toInt()' + : 'buffer.readUint64()'; case TypeIds.varUint64: return field.type.isDartCoreInt - ? 'buffer.readVarUint64()' - : 'Uint64(buffer.readVarUint64())'; + ? 'buffer.readVarUint64().toInt()' + : 'buffer.readVarUint64()'; case TypeIds.taggedUint64: return field.type.isDartCoreInt - ? 'buffer.readTaggedUint64()' - : 'Uint64(buffer.readTaggedUint64())'; + ? 'buffer.readTaggedUint64().toInt()' + : 'buffer.readTaggedUint64()'; case TypeIds.float16: return 'buffer.readFloat16()'; case TypeIds.bfloat16: @@ -1436,7 +1453,7 @@ GeneratedFieldType( case TypeIds.int32Array: return 'readGeneratedTypedArrayValue(context, 4, (bytes) => bytes.buffer.asInt32List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 4))'; case TypeIds.int64Array: - return 'readGeneratedTypedArrayValue(context, 8, (bytes) => bytes.buffer.asInt64List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 8))'; + return 'readGeneratedTypedArrayValue(context, 8, (bytes) => Int64List.view(bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 8))'; case TypeIds.uint8Array: return 'readGeneratedBinaryValue(context)'; case TypeIds.uint16Array: @@ -1448,7 +1465,7 @@ GeneratedFieldType( case TypeIds.uint32Array: return 'readGeneratedTypedArrayValue(context, 4, (bytes) => bytes.buffer.asUint32List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 4))'; case TypeIds.uint64Array: - return 'readGeneratedTypedArrayValue(context, 8, (bytes) => bytes.buffer.asUint64List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 8))'; + return 'readGeneratedTypedArrayValue(context, 8, (bytes) => Uint64List.view(bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 8))'; case TypeIds.float32Array: return 'readGeneratedTypedArrayValue(context, 4, (bytes) => bytes.buffer.asFloat32List(bytes.offsetInBytes, bytes.lengthInBytes ~/ 4))'; case TypeIds.float64Array: @@ -1482,11 +1499,17 @@ GeneratedFieldType( ? '$cursorExpression.readVarInt32()' : 'Int32($cursorExpression.readVarInt32())'; case TypeIds.int64: - return '$cursorExpression.readInt64()'; + return field.type.isDartCoreInt + ? '$cursorExpression.readInt64().toInt()' + : '$cursorExpression.readInt64()'; case TypeIds.varInt64: - return '$cursorExpression.readVarInt64()'; + return field.type.isDartCoreInt + ? '$cursorExpression.readVarInt64().toInt()' + : '$cursorExpression.readVarInt64()'; case TypeIds.taggedInt64: - return '$cursorExpression.readTaggedInt64()'; + return field.type.isDartCoreInt + ? '$cursorExpression.readTaggedInt64().toInt()' + : '$cursorExpression.readTaggedInt64()'; case TypeIds.uint8: return field.type.isDartCoreInt ? '$cursorExpression.readUint8()' @@ -1505,16 +1528,16 @@ GeneratedFieldType( : 'Uint32($cursorExpression.readVarUint32())'; case TypeIds.uint64: return field.type.isDartCoreInt - ? '$cursorExpression.readUint64()' - : 'Uint64($cursorExpression.readUint64())'; + ? '$cursorExpression.readUint64().toInt()' + : '$cursorExpression.readUint64()'; case TypeIds.varUint64: return field.type.isDartCoreInt - ? '$cursorExpression.readVarUint64()' - : 'Uint64($cursorExpression.readVarUint64())'; + ? '$cursorExpression.readVarUint64().toInt()' + : '$cursorExpression.readVarUint64()'; case TypeIds.taggedUint64: return field.type.isDartCoreInt - ? '$cursorExpression.readTaggedUint64()' - : 'Uint64($cursorExpression.readTaggedUint64())'; + ? '$cursorExpression.readTaggedUint64().toInt()' + : '$cursorExpression.readTaggedUint64()'; case TypeIds.float16: return '$cursorExpression.readFloat16()'; case TypeIds.bfloat16: @@ -1526,7 +1549,7 @@ GeneratedFieldType( case TypeIds.float64: return '$cursorExpression.readFloat64()'; case TypeIds.date: - return 'LocalDate.fromEpochDay($cursorExpression.readVarInt64())'; + return 'LocalDate.fromEpochDay($cursorExpression.readVarInt64().toInt())'; case TypeIds.duration: return 'readGeneratedDurationFromWire($cursorExpression.readVarInt64(), $cursorExpression.readInt32())'; case TypeIds.timestamp: @@ -1568,15 +1591,33 @@ GeneratedFieldType( _GeneratedFieldSpec field, String valueExpression, ) { - if (field.type.isDartCoreInt || - field.type.isDartCoreDouble || + if (field.type.isDartCoreInt) { + switch (field.fieldType.typeId) { + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + return 'Int64($valueExpression)'; + case TypeIds.uint64: + case TypeIds.varUint64: + case TypeIds.taggedUint64: + return 'Uint64($valueExpression)'; + default: + return valueExpression; + } + } + if (field.type.isDartCoreDouble || field.type.isDartCoreBool || field.type.isDartCoreString) { return valueExpression; } switch (field.fieldType.typeId) { + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + case TypeIds.uint64: + case TypeIds.varUint64: + case TypeIds.taggedUint64: case TypeIds.float16: - return valueExpression; case TypeIds.bfloat16: return valueExpression; default: @@ -1607,6 +1648,7 @@ GeneratedFieldType( } return _GeneratedFieldTypeSpec( typeLiteral: fieldType.typeLiteral, + declaredTypeName: fieldType.declaredTypeName, typeId: fieldType.typeId, nullable: false, ref: fieldType.ref, @@ -2101,6 +2143,8 @@ GeneratedFieldType( return TypeIds.uint32; case 'Uint64': return TypeIds.uint64; + case 'Int64': + return TypeIds.varInt64; case 'Float16': return TypeIds.float16; case 'Bfloat16': @@ -2381,6 +2425,7 @@ final class _DirectGeneratedWriteReservationRun { final class _GeneratedFieldTypeSpec { final String typeLiteral; + final String? declaredTypeName; final int typeId; final bool nullable; final bool ref; @@ -2389,6 +2434,7 @@ final class _GeneratedFieldTypeSpec { const _GeneratedFieldTypeSpec({ required this.typeLiteral, + this.declaredTypeName, required this.typeId, required this.nullable, required this.ref, diff --git a/dart/packages/fory/lib/src/codegen/generated_support.dart b/dart/packages/fory/lib/src/codegen/generated_support.dart index 7fbc1de1bb..0e4e462e6a 100644 --- a/dart/packages/fory/lib/src/codegen/generated_support.dart +++ b/dart/packages/fory/lib/src/codegen/generated_support.dart @@ -36,13 +36,7 @@ import 'package:fory/src/serializer/struct_serializer.dart'; import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/time_serializers.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; - -final BigInt _generatedCursorMask64Big = (BigInt.one << 64) - BigInt.one; -final BigInt _generatedCursorSevenBitMaskBig = BigInt.from(0x7f); -final BigInt _generatedCursorByteMaskBig = BigInt.from(0xff); -const bool _generatedCursorUseBigIntVarint64 = - bool.fromEnvironment('dart.library.js_interop') || - bool.fromEnvironment('dart.library.js_util'); +import 'package:fory/src/util/int64_codec.dart'; @internal final class GeneratedWriteCursor { @@ -107,13 +101,13 @@ final class GeneratedWriteCursor { _offset += 4; } - void writeInt64(int value) { - _view.setInt64(_offset, value, Endian.little); + void writeInt64(Int64 value) { + writeInt64LittleEndian(_view, _offset, value); _offset += 8; } - void writeUint64(int value) { - _view.setUint64(_offset, value, Endian.little); + void writeUint64(Uint64 value) { + writeUint64LittleEndian(_view, _offset, value); _offset += 8; } @@ -147,75 +141,37 @@ final class GeneratedWriteCursor { } void writeVarInt32(int value) { - writeVarUint32((value << 1) ^ (value >> 31)); - } - - void writeVarUint64(int value) { - if (!_generatedCursorUseBigIntVarint64) { - var remaining = value; - for (var index = 0; index < 8; index += 1) { - final chunk = remaining & 0x7f; - remaining = remaining >>> 7; - if (remaining == 0) { - _bytes[_offset] = chunk; - _offset += 1; - return; - } - _bytes[_offset] = chunk | 0x80; - _offset += 1; - } - _bytes[_offset] = remaining & 0xff; + writeVarUint32(((value << 1) ^ (value >> 31)).toUnsigned(32)); + } + + void writeVarUint64(Uint64 value) { + writeVarUint64Bytes(value, (byte) { + _bytes[_offset] = byte; _offset += 1; - return; - } - _writeVarUint64BigInt(BigInt.from(value) & _generatedCursorMask64Big); + }); } - void writeVarInt64(int value) { - if (!_generatedCursorUseBigIntVarint64) { - writeVarUint64((value << 1) ^ (value >> 63)); - return; - } - final signed = BigInt.from(value); - final zigZag = - ((signed << 1) ^ BigInt.from(value >> 63)) & _generatedCursorMask64Big; - _writeVarUint64BigInt(zigZag); + void writeVarInt64(Int64 value) { + writeVarUint64(zigZagEncodeInt64(value)); } - void writeTaggedInt64(int value) { + void writeTaggedInt64(Int64 value) { if (value >= -0x40000000 && value <= 0x3fffffff) { - writeInt32(value << 1); + writeInt32((value.toInt() << 1).toSigned(32)); return; } writeUint8(0x01); writeInt64(value); } - void writeTaggedUint64(int value) { + void writeTaggedUint64(Uint64 value) { if (value >= 0 && value <= 0x7fffffff) { - writeInt32(value << 1); + writeInt32(value.toInt() << 1); return; } writeUint8(0x01); writeUint64(value); } - - void _writeVarUint64BigInt(BigInt value) { - var remaining = value & _generatedCursorMask64Big; - for (var index = 0; index < 8; index += 1) { - final chunk = (remaining & _generatedCursorSevenBitMaskBig).toInt(); - remaining >>= 7; - if (remaining == BigInt.zero) { - _bytes[_offset] = chunk; - _offset += 1; - return; - } - _bytes[_offset] = chunk | 0x80; - _offset += 1; - } - _bytes[_offset] = (remaining & _generatedCursorByteMaskBig).toInt(); - _offset += 1; - } } @internal @@ -280,14 +236,14 @@ final class GeneratedReadCursor { return value; } - int readInt64() { - final value = _view.getInt64(_offset, Endian.little); + Int64 readInt64() { + final value = readInt64LittleEndian(_view, _offset); _offset += 8; return value; } - int readUint64() { - final value = _view.getUint64(_offset, Endian.little); + Uint64 readUint64() { + final value = readUint64LittleEndian(_view, _offset); _offset += 8; return value; } @@ -323,82 +279,46 @@ final class GeneratedReadCursor { int readVarInt32() { final value = readVarUint32(); - return (value >>> 1) ^ -(value & 1); - } - - int readVarUint64() { - if (!_generatedCursorUseBigIntVarint64) { - var shift = 0; - var result = 0; - while (shift < 56) { - final byte = readUint8(); - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | (readUint8() << 56); - } - return _readVarUint64BigInt().toInt(); + return ((value >>> 1) ^ -(value & 1)).toSigned(32); } - int readVarInt64() { - if (!_generatedCursorUseBigIntVarint64) { - final encoded = readVarUint64(); - return (encoded >>> 1) ^ -(encoded & 1); - } - final encoded = _readVarUint64BigInt(); - final magnitude = (encoded >> 1).toInt(); - if ((encoded & BigInt.one) == BigInt.zero) { - return magnitude; - } - return -magnitude - 1; + Uint64 readVarUint64() { + return readVarUint64Bytes(readUint8); } - int readTaggedInt64() { + Int64 readVarInt64() { + return zigZagDecodeInt64(readVarUint64()); + } + + Int64 readTaggedInt64() { final readIndex = _offset; final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _offset = readIndex + 4; - return first >> 1; + return Int64(first.toSigned(32) ~/ 2); } - final value = _view.getInt64(readIndex + 1, Endian.little); + final value = readInt64LittleEndian(_view, readIndex + 1); _offset = readIndex + 9; return value; } - int readTaggedUint64() { + Uint64 readTaggedUint64() { final readIndex = _offset; final first = _view.getUint32(readIndex, Endian.little); if ((first & 1) == 0) { _offset = readIndex + 4; - return first >>> 1; + return Uint64(first >>> 1); } - final value = _view.getUint64(readIndex + 1, Endian.little); + final value = readUint64LittleEndian(_view, readIndex + 1); _offset = readIndex + 9; return value; } - - BigInt _readVarUint64BigInt() { - var shift = 0; - var result = BigInt.zero; - while (shift < 56) { - final byte = readUint8(); - result |= BigInt.from(byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | - ((BigInt.from(readUint8()) & _generatedCursorByteMaskBig) << 56); - } } @internal final class GeneratedFieldType { final Type type; + final String? declaredTypeName; final int typeId; final bool nullable; final bool ref; @@ -407,6 +327,7 @@ final class GeneratedFieldType { const GeneratedFieldType({ required this.type, + this.declaredTypeName, required this.typeId, required this.nullable, required this.ref, @@ -417,6 +338,7 @@ final class GeneratedFieldType { meta_types.FieldType toFieldType() { return meta_types.FieldType( type: type, + declaredTypeName: declaredTypeName, typeId: typeId, nullable: nullable, ref: ref, @@ -633,7 +555,7 @@ Decimal readGeneratedDecimalValue(ReadContext context) { } @internal -int generatedDurationWireSeconds(Duration value) { +Int64 generatedDurationWireSeconds(Duration value) { return durationWireSeconds(value); } @@ -643,7 +565,7 @@ int generatedDurationWireNanoseconds(Duration value) { } @internal -Duration readGeneratedDurationFromWire(int seconds, int nanoseconds) { +Duration readGeneratedDurationFromWire(Int64 seconds, int nanoseconds) { return durationFromWire(seconds, nanoseconds); } @@ -663,7 +585,7 @@ int generatedTimestampWireNanoseconds(Timestamp value) { } @internal -int generatedDateTimeWireSeconds(DateTime value) { +Int64 generatedDateTimeWireSeconds(DateTime value) { return dateTimeWireSeconds(value); } @@ -673,12 +595,12 @@ int generatedDateTimeWireNanoseconds(DateTime value) { } @internal -Timestamp readGeneratedTimestampFromWire(int seconds, int nanoseconds) { +Timestamp readGeneratedTimestampFromWire(Int64 seconds, int nanoseconds) { return timestampFromWire(seconds, nanoseconds); } @internal -DateTime readGeneratedDateTimeFromWire(int seconds, int nanoseconds) { +DateTime readGeneratedDateTimeFromWire(Int64 seconds, int nanoseconds) { return dateTimeFromWire(seconds, nanoseconds); } diff --git a/dart/packages/fory/lib/src/context/meta_string_reader.dart b/dart/packages/fory/lib/src/context/meta_string_reader.dart index ba44a68c85..fb63ddc570 100644 --- a/dart/packages/fory/lib/src/context/meta_string_reader.dart +++ b/dart/packages/fory/lib/src/context/meta_string_reader.dart @@ -22,6 +22,7 @@ import 'dart:typed_data'; import 'package:fory/src/buffer.dart'; import 'package:fory/src/meta/meta_string.dart'; import 'package:fory/src/resolver/type_resolver.dart'; +import 'package:fory/src/types/int64.dart'; typedef _MetaStringWords = ({int length, int word0, int word1, int word2, int word3}); @@ -30,8 +31,8 @@ typedef _MetaStringWords = final class MetaStringReader { final TypeResolver _typeResolver; final List _dynamicReadMetaStrings = []; - final Map _bigMetaStrings = - {}; + final Map _bigMetaStrings = + {}; final Map> _smallMetaStrings = >{}; @@ -79,7 +80,7 @@ final class MetaStringReader { } final encoded = _typeResolver.internEncodedMetaString( buffer.copyBytes(length), - encoding: hash & 0xff, + encoding: (hash & 0xff).toInt(), ); _bigMetaStrings[hash] = encoded; return encoded; diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart index 1945e5ae0c..821f574077 100644 --- a/dart/packages/fory/lib/src/context/read_context.dart +++ b/dart/packages/fory/lib/src/context/read_context.dart @@ -32,12 +32,15 @@ import 'package:fory/src/serializer/map_serializers.dart'; import 'package:fory/src/serializer/primitive_serializers.dart'; import 'package:fory/src/serializer/scalar_serializers.dart'; import 'package:fory/src/serializer/serializer.dart'; +import 'package:fory/src/serializer/serializer_support.dart'; import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/time_serializers.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; import 'package:fory/src/types/bfloat16.dart'; import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/timestamp.dart'; +import 'package:fory/src/types/uint64.dart'; /// Read-side serializer context. /// @@ -124,7 +127,7 @@ final class ReadContext { } return value; } - + int get depth => _depth; /// Records entry into one more nested read frame. @@ -156,7 +159,7 @@ final class ReadContext { int readInt32() => _buffer.readInt32(); /// Reads a signed little-endian 64-bit integer. - int readInt64() => _buffer.readInt64(); + Int64 readInt64() => _buffer.readInt64(); /// Reads a half-precision floating-point value. Float16 readFloat16() => _buffer.readFloat16(); @@ -177,16 +180,16 @@ final class ReadContext { int readVarUint32() => _buffer.readVarUint32(); /// Reads a zig-zag encoded signed 64-bit varint. - int readVarInt64() => _buffer.readVarInt64(); + Int64 readVarInt64() => _buffer.readVarInt64(); /// Reads a tagged signed 64-bit integer. - int readTaggedInt64() => _buffer.readTaggedInt64(); + Int64 readTaggedInt64() => _buffer.readTaggedInt64(); /// Reads an unsigned 64-bit varint. - int readVarUint64() => _buffer.readVarUint64(); + Uint64 readVarUint64() => _buffer.readVarUint64(); /// Reads a tagged unsigned 64-bit integer. - int readTaggedUint64() => _buffer.readTaggedUint64(); + Uint64 readTaggedUint64() => _buffer.readTaggedUint64(); /// Binds [value] to the most recently preserved Ref slot. void reference(Object? value) { @@ -299,7 +302,11 @@ final class ReadContext { required bool hasPreservedRef, }) { if (TypeIds.isPrimitive(resolved.typeId)) { - return PrimitiveSerializer.readPayload(this, resolved.typeId); + return convertResolvedPrimitiveValue( + PrimitiveSerializer.readPayload(this, resolved.typeId), + resolved, + declaredFieldType, + ); } switch (resolved.typeId) { case TypeIds.none: diff --git a/dart/packages/fory/lib/src/context/write_context.dart b/dart/packages/fory/lib/src/context/write_context.dart index f5fec0e484..279fd44123 100644 --- a/dart/packages/fory/lib/src/context/write_context.dart +++ b/dart/packages/fory/lib/src/context/write_context.dart @@ -40,8 +40,10 @@ import 'package:fory/src/serializer/time_serializers.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; import 'package:fory/src/types/bfloat16.dart'; import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/local_date.dart'; import 'package:fory/src/types/timestamp.dart'; +import 'package:fory/src/types/uint64.dart'; /// Write-side serializer context. /// @@ -137,7 +139,7 @@ final class WriteContext { void writeInt32(int value) => _buffer.writeInt32(value); /// Writes a signed little-endian 64-bit integer. - void writeInt64(int value) => _buffer.writeInt64(value); + void writeInt64(Int64 value) => _buffer.writeInt64(value); /// Writes a half-precision floating-point value. void writeFloat16(Float16 value) => _buffer.writeFloat16(value); @@ -158,16 +160,16 @@ final class WriteContext { void writeVarUint32(int value) => _buffer.writeVarUint32(value); /// Writes a zig-zag encoded signed 64-bit varint. - void writeVarInt64(int value) => _buffer.writeVarInt64(value); + void writeVarInt64(Int64 value) => _buffer.writeVarInt64(value); /// Writes a tagged signed 64-bit integer. - void writeTaggedInt64(int value) => _buffer.writeTaggedInt64(value); + void writeTaggedInt64(Int64 value) => _buffer.writeTaggedInt64(value); /// Writes an unsigned 64-bit varint. - void writeVarUint64(int value) => _buffer.writeVarUint64(value); + void writeVarUint64(Uint64 value) => _buffer.writeVarUint64(value); /// Writes a tagged unsigned 64-bit integer. - void writeTaggedUint64(int value) => _buffer.writeTaggedUint64(value); + void writeTaggedUint64(Uint64 value) => _buffer.writeTaggedUint64(value); /// Writes a non-null string payload without adding type metadata. void writeString(String value) => StringSerializer.writePayload(this, value); diff --git a/dart/packages/fory/lib/src/meta/field_type.dart b/dart/packages/fory/lib/src/meta/field_type.dart index 140f086899..c4359856d3 100644 --- a/dart/packages/fory/lib/src/meta/field_type.dart +++ b/dart/packages/fory/lib/src/meta/field_type.dart @@ -21,6 +21,7 @@ import 'package:fory/src/meta/type_ids.dart'; final class FieldType { final Type type; + final String? declaredTypeName; final int typeId; final bool nullable; final bool ref; @@ -29,6 +30,7 @@ final class FieldType { const FieldType({ required this.type, + this.declaredTypeName, required this.typeId, required this.nullable, required this.ref, @@ -51,6 +53,7 @@ final class FieldType { } return FieldType( type: type, + declaredTypeName: declaredTypeName, typeId: typeId, nullable: nullable, ref: ref, diff --git a/dart/packages/fory/lib/src/meta/meta_string.dart b/dart/packages/fory/lib/src/meta/meta_string.dart index 935b4b47fc..abd60d517a 100644 --- a/dart/packages/fory/lib/src/meta/meta_string.dart +++ b/dart/packages/fory/lib/src/meta/meta_string.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/util/hash_util.dart'; const int metaStringUtf8Encoding = 0; @@ -54,7 +55,7 @@ const List _fieldNameCompactEncodings = [ final class EncodedMetaString { final Uint8List bytes; final int encoding; - final int hash; + final Int64 hash; final int firstWord0; final int firstWord1; final int secondWord0; diff --git a/dart/packages/fory/lib/src/meta/type_def.dart b/dart/packages/fory/lib/src/meta/type_def.dart index 9344223214..cd84804f44 100644 --- a/dart/packages/fory/lib/src/meta/type_def.dart +++ b/dart/packages/fory/lib/src/meta/type_def.dart @@ -20,11 +20,12 @@ import 'dart:typed_data'; import 'package:fory/src/meta/field_info.dart'; +import 'package:fory/src/types/int64.dart'; final class TypeDef { final bool evolving; final List fields; - final int header; + final Int64 header; final Uint8List encoded; const TypeDef({ diff --git a/dart/packages/fory/lib/src/meta/type_meta.dart b/dart/packages/fory/lib/src/meta/type_meta.dart index 5a40c85060..50fb5d33e8 100644 --- a/dart/packages/fory/lib/src/meta/type_meta.dart +++ b/dart/packages/fory/lib/src/meta/type_meta.dart @@ -24,6 +24,7 @@ import 'package:fory/src/config.dart'; import 'package:fory/src/meta/meta_string.dart'; import 'package:fory/src/meta/type_ids.dart'; import 'package:fory/src/resolver/type_resolver.dart'; +import 'package:fory/src/types/int64.dart'; /// Wire-level type metadata for one value. final class WireTypeMeta { @@ -52,12 +53,12 @@ final class WireTypeMeta { } final class TypeHeader { - final int value; + final Int64 value; const TypeHeader(this.value); int readMetaSize(Buffer buffer) { - final lowBits = value & 0xff; + final lowBits = (value & 0xff).toInt(); if (lowBits == 0xff) { return 0xff + buffer.readVarUint32Small14(); } @@ -72,8 +73,8 @@ final class TypeHeader { final class ParsedTypeMetaCache { static const int maxEntries = 8192; - final LinkedHashMap _entries = LinkedHashMap(); - int? _lastHeader; + final LinkedHashMap _entries = LinkedHashMap(); + Int64? _lastHeader; TypeInfo? _lastResolved; TypeInfo? lookup(TypeHeader header) { diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index 3c39a06540..a62269d1c7 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -48,6 +48,7 @@ import 'package:fory/src/types/float16.dart'; import 'package:fory/src/types/float32.dart'; import 'package:fory/src/types/int16.dart'; import 'package:fory/src/types/int32.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/int8.dart'; import 'package:fory/src/types/local_date.dart'; import 'package:fory/src/types/timestamp.dart'; @@ -323,6 +324,9 @@ final class TypeResolver { if (value is Int32) { return _builtin(Int32, TypeIds.varInt32); } + if (value is Int64 && value is! int) { + return _builtin(Int64, TypeIds.int64); + } if (value is int) { return _builtin(int, TypeIds.varInt64); } @@ -467,7 +471,7 @@ final class TypeResolver { case TypeIds.bfloat16Array: case TypeIds.float32Array: case TypeIds.float64Array: - return _builtin(fieldType.type, fieldType.typeId); + return _builtin(_builtinTypeForFieldType(fieldType), fieldType.typeId); default: return _registeredByType[fieldType.type]; } @@ -858,14 +862,17 @@ final class TypeResolver { return wireTypeMetaForResolved(sharedTypes[index]); } final header = TypeHeader(buffer.readInt64()); - final cached = _parsedTypeMetaCache.lookup(header); + final cached = + config.compatible ? null : _parsedTypeMetaCache.lookup(header); if (cached != null) { header.skipRemaining(buffer); sharedTypes.add(cached); return wireTypeMetaForResolved(cached); } final resolved = _readTypeDefWithHeader(buffer, header); - _parsedTypeMetaCache.remember(header, resolved); + if (!config.compatible) { + _parsedTypeMetaCache.remember(header, resolved); + } sharedTypes.add(resolved); return wireTypeMetaForResolved(resolved); } @@ -988,6 +995,7 @@ final class TypeResolver { } return FieldType( type: Object, + declaredTypeName: null, typeId: typeId, nullable: nullable, ref: ref, @@ -1019,11 +1027,11 @@ final class TypeResolver { case TypeIds.varInt32: return _builtin(Int32, TypeIds.varInt32); case TypeIds.int64: - return _builtin(int, TypeIds.int64); + return _builtin(Int64, TypeIds.int64); case TypeIds.varInt64: return _builtin(int, TypeIds.varInt64); case TypeIds.taggedInt64: - return _builtin(int, TypeIds.taggedInt64); + return _builtin(Int64, TypeIds.taggedInt64); case TypeIds.uint8: return _builtin(Uint8, TypeIds.uint8); case TypeIds.uint16: @@ -1238,9 +1246,12 @@ final class TypeResolver { if (type == Int32) { return TypeIds.varInt32; } - if (type == int) { + if (type == Int64) { return TypeIds.varInt64; } + if (type == Int64List) { + return TypeIds.int64Array; + } if (type == Uint8) { return TypeIds.uint8; } @@ -1422,6 +1433,30 @@ final class TypeResolver { return true; } + Type _builtinTypeForFieldType(FieldType fieldType) { + final declaredTypeName = fieldType.declaredTypeName; + if (declaredTypeName != null) { + if (_matchesDeclaredTypeName(declaredTypeName, 'Int64')) { + return Int64; + } + if (_matchesDeclaredTypeName(declaredTypeName, 'Uint64')) { + return Uint64; + } + if (_matchesDeclaredTypeName(declaredTypeName, 'Int64List')) { + return Int64List; + } + if (_matchesDeclaredTypeName(declaredTypeName, 'Uint64List')) { + return Uint64List; + } + } + return fieldType.type; + } + + bool _matchesDeclaredTypeName(String declaredTypeName, String typeName) { + return declaredTypeName == typeName || + declaredTypeName.endsWith('.$typeName'); + } + bool _matchesNamedWireType(TypeInfo resolved, int wireTypeId) { if (!resolved.isNamed) { return false; diff --git a/dart/packages/fory/lib/src/serializer/collection_serializers.dart b/dart/packages/fory/lib/src/serializer/collection_serializers.dart index d4d59a6eaa..f521a2cd01 100644 --- a/dart/packages/fory/lib/src/serializer/collection_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/collection_serializers.dart @@ -71,7 +71,11 @@ Object? readTypeInfoValue( bool hasPreservedRef = false, }) { if (TypeIds.isPrimitive(typeInfo.typeId)) { - return PrimitiveSerializer.readPayload(context, typeInfo.typeId); + return convertResolvedPrimitiveValue( + PrimitiveSerializer.readPayload(context, typeInfo.typeId), + typeInfo, + fieldType, + ); } if (typeInfo.typeId == TypeIds.string) { return StringSerializer.readPayload(context); diff --git a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart index 2b00b68e6d..4d671d78a3 100644 --- a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart @@ -26,6 +26,7 @@ import 'package:fory/src/types/float16.dart'; import 'package:fory/src/types/float32.dart'; import 'package:fory/src/types/int16.dart'; import 'package:fory/src/types/int32.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/int8.dart'; import 'package:fory/src/types/uint16.dart'; import 'package:fory/src/types/uint32.dart'; @@ -77,13 +78,13 @@ final class PrimitiveSerializer extends Serializer { buffer.writeVarInt32(value is Int32 ? value.value : value as int); return; case TypeIds.int64: - buffer.writeInt64(value as int); + buffer.writeInt64(value is Int64 ? value : Int64(value as int)); return; case TypeIds.varInt64: - buffer.writeVarInt64(value as int); + buffer.writeVarInt64(value is Int64 ? value : Int64(value as int)); return; case TypeIds.taggedInt64: - buffer.writeTaggedInt64(value as int); + buffer.writeTaggedInt64(value is Int64 ? value : Int64(value as int)); return; case TypeIds.uint8: buffer.writeUint8(value is Uint8 ? value.value : value as int); @@ -98,13 +99,15 @@ final class PrimitiveSerializer extends Serializer { buffer.writeVarUint32(value is Uint32 ? value.value : value as int); return; case TypeIds.uint64: - buffer.writeUint64(value is Uint64 ? value.value : value as int); + buffer.writeUint64(value is Uint64 ? value : Uint64(value as int)); return; case TypeIds.varUint64: - buffer.writeVarUint64(value is Uint64 ? value.value : value as int); + buffer.writeVarUint64(value is Uint64 ? value : Uint64(value as int)); return; case TypeIds.taggedUint64: - buffer.writeTaggedUint64(value is Uint64 ? value.value : value as int); + buffer.writeTaggedUint64( + value is Uint64 ? value : Uint64(value as int), + ); return; case TypeIds.float16: buffer.writeFloat16(value as Float16); @@ -154,11 +157,11 @@ final class PrimitiveSerializer extends Serializer { case TypeIds.varUint32: return Uint32(buffer.readVarUint32()); case TypeIds.uint64: - return Uint64(buffer.readUint64()); + return buffer.readUint64(); case TypeIds.varUint64: - return Uint64(buffer.readVarUint64()); + return buffer.readVarUint64(); case TypeIds.taggedUint64: - return Uint64(buffer.readTaggedUint64()); + return buffer.readTaggedUint64(); case TypeIds.float16: return buffer.readFloat16(); case TypeIds.bfloat16: @@ -194,15 +197,16 @@ const PrimitiveSerializer varInt32Serializer = TypeIds.varInt32, supportsRef: false, ); -const PrimitiveSerializer int64Serializer = PrimitiveSerializer( +const PrimitiveSerializer int64Serializer = PrimitiveSerializer( TypeIds.int64, supportsRef: false, ); -const PrimitiveSerializer varInt64Serializer = PrimitiveSerializer( +const PrimitiveSerializer varInt64Serializer = PrimitiveSerializer( TypeIds.varInt64, supportsRef: false, ); -const PrimitiveSerializer taggedInt64Serializer = PrimitiveSerializer( +const PrimitiveSerializer taggedInt64Serializer = + PrimitiveSerializer( TypeIds.taggedInt64, supportsRef: false, ); diff --git a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart index 0fbfee8053..3cb449fdd9 100644 --- a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart @@ -22,6 +22,9 @@ import 'dart:typed_data'; import 'package:fory/src/context/read_context.dart'; import 'package:fory/src/context/write_context.dart'; import 'package:fory/src/serializer/serializer.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; +import 'package:fory/src/util/int64_codec.dart'; import 'package:fory/src/util/string_util.dart'; import 'package:fory/src/types/decimal.dart'; @@ -157,8 +160,7 @@ final class DecimalSerializer extends Serializer { final unscaled = value.unscaledValue; buffer.writeVarInt32(value.scale); if (_canUseSmallDecimalEncoding(unscaled)) { - final smallValue = unscaled.toInt(); - final zigZag = (smallValue << 1) ^ (smallValue >> 63); + final zigZag = zigZagEncodeInt64(Int64.fromBigInt(unscaled)); buffer.writeVarUint64(zigZag << 1); return; } @@ -166,21 +168,20 @@ final class DecimalSerializer extends Serializer { final payload = _decimalMagnitudeToCanonicalLittleEndian(unscaled.abs()); final sign = unscaled.isNegative ? 1 : 0; final meta = (payload.length << 1) | sign; - buffer.writeVarUint64((meta << 1) | 1); + buffer.writeVarUint64(Uint64((meta << 1) | 1)); buffer.writeBytes(payload); } static Decimal readPayload(ReadContext context) { final scale = context.buffer.readVarInt32(); final header = context.buffer.readVarUint64(); - if ((header & 1) == 0) { + if ((header.low32 & 1) == 0) { final zigZag = header >>> 1; - final unscaled = (zigZag >>> 1) ^ -(zigZag & 1); - return Decimal(BigInt.from(unscaled), scale); + return Decimal(zigZagDecodeInt64(zigZag).toBigInt(), scale); } final meta = header >>> 1; - final length = meta >>> 1; + final length = (meta >>> 1).toInt(); if (length <= 0) { throw StateError('Invalid decimal magnitude length $length.'); } @@ -194,7 +195,7 @@ final class DecimalSerializer extends Serializer { if (magnitude == BigInt.zero) { throw StateError('Big decimal encoding must not represent zero.'); } - final sign = meta & 1; + final sign = (meta & 1).toInt(); return Decimal(sign == 0 ? magnitude : -magnitude, scale); } } diff --git a/dart/packages/fory/lib/src/serializer/serializer_support.dart b/dart/packages/fory/lib/src/serializer/serializer_support.dart index 3ca7228bcb..32b1dccd74 100644 --- a/dart/packages/fory/lib/src/serializer/serializer_support.dart +++ b/dart/packages/fory/lib/src/serializer/serializer_support.dart @@ -28,6 +28,7 @@ import 'package:fory/src/serializer/serialization_field_info.dart'; import 'package:fory/src/types/float32.dart'; import 'package:fory/src/types/int16.dart'; import 'package:fory/src/types/int32.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/int8.dart'; import 'package:fory/src/types/uint16.dart'; import 'package:fory/src/types/uint32.dart'; @@ -64,6 +65,12 @@ Object convertPrimitiveFieldValue(Object value, FieldType fieldType) { case TypeIds.int32: case TypeIds.varInt32: return (value as Int32).value; + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + return _declares64BitWrapper(fieldType) + ? value + : (value as Int64).toInt(); case TypeIds.uint8: return (value as Uint8).value; case TypeIds.uint16: @@ -74,7 +81,9 @@ Object convertPrimitiveFieldValue(Object value, FieldType fieldType) { case TypeIds.uint64: case TypeIds.varUint64: case TypeIds.taggedUint64: - return (value as Uint64).value; + return _declares64BitWrapper(fieldType) + ? value + : (value as Uint64).toInt(); default: return value; } @@ -85,6 +94,69 @@ Object convertPrimitiveFieldValue(Object value, FieldType fieldType) { return value; } +bool _declares64BitWrapper(FieldType fieldType) { + final declaredTypeName = fieldType.declaredTypeName; + if (declaredTypeName == null) { + return false; + } + return declaredTypeName == 'Int64' || + declaredTypeName.endsWith('.Int64') || + declaredTypeName == 'Uint64' || + declaredTypeName.endsWith('.Uint64'); +} + +Object convertResolvedPrimitiveValue( + Object value, + TypeInfo resolved, [ + FieldType? fieldType, +]) { + if (fieldType != null && + _declares64BitWrapper(fieldType) && + (resolved.typeId == TypeIds.int64 || + resolved.typeId == TypeIds.varInt64 || + resolved.typeId == TypeIds.taggedInt64 || + resolved.typeId == TypeIds.uint64 || + resolved.typeId == TypeIds.varUint64 || + resolved.typeId == TypeIds.taggedUint64)) { + return value; + } + if (resolved.type == int) { + switch (resolved.typeId) { + case TypeIds.int8: + return (value as Int8).value; + case TypeIds.int16: + return (value as Int16).value; + case TypeIds.int32: + case TypeIds.varInt32: + return (value as Int32).value; + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + return (value as Int64).toInt(); + case TypeIds.uint8: + return (value as Uint8).value; + case TypeIds.uint16: + return (value as Uint16).value; + case TypeIds.uint32: + case TypeIds.varUint32: + return (value as Uint32).value; + case TypeIds.uint64: + case TypeIds.varUint64: + case TypeIds.taggedUint64: + return (value as Uint64).toInt(); + default: + break; + } + } + if (resolved.type == double && resolved.typeId == TypeIds.float32) { + return (value as Float32).value; + } + if (fieldType != null) { + return convertPrimitiveFieldValue(value, fieldType); + } + return value; +} + void writeFieldValue( WriteContext context, SerializationFieldInfo field, @@ -284,6 +356,7 @@ FieldInfo mergeCompatibleWriteField( } return FieldType( type: local.type, + declaredTypeName: local.declaredTypeName, typeId: remote.typeId, nullable: remote.nullable, ref: remote.ref, @@ -319,6 +392,7 @@ FieldInfo mergeCompatibleReadField( } return FieldType( type: local.type, + declaredTypeName: local.declaredTypeName, typeId: remote.typeId, nullable: remote.nullable, ref: remote.ref, diff --git a/dart/packages/fory/lib/src/serializer/time_serializers.dart b/dart/packages/fory/lib/src/serializer/time_serializers.dart index 985f1e57cd..74de18416a 100644 --- a/dart/packages/fory/lib/src/serializer/time_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/time_serializers.dart @@ -20,6 +20,7 @@ import 'package:fory/src/context/read_context.dart'; import 'package:fory/src/context/write_context.dart'; import 'package:fory/src/serializer/serializer.dart'; +import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/local_date.dart'; import 'package:fory/src/types/timestamp.dart'; @@ -60,12 +61,12 @@ void _validateDateTimeNanoseconds(int nanoseconds) { } } -int durationWireSeconds(Duration value) { +Int64 durationWireSeconds(Duration value) { final exact = _exactDurationWire[value]; if (exact != null && exact.microseconds == value.inMicroseconds) { - return exact.seconds; + return Int64(exact.seconds); } - return value.inMicroseconds ~/ Duration.microsecondsPerSecond; + return Int64(value.inMicroseconds ~/ Duration.microsecondsPerSecond); } int durationWireNanoseconds(Duration value) { @@ -76,19 +77,21 @@ int durationWireNanoseconds(Duration value) { return value.inMicroseconds.remainder(Duration.microsecondsPerSecond) * 1000; } -Duration durationFromWire(int seconds, int nanoseconds) { +Duration durationFromWire(Int64 seconds, int nanoseconds) { _validateDurationNanoseconds(nanoseconds); if ((seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0)) { throw StateError( 'Duration wire value has inconsistent signs: seconds=$seconds, nanoseconds=$nanoseconds.', ); } - final totalNanoseconds = seconds * _nanosecondsPerSecond + nanoseconds; - final microseconds = totalNanoseconds ~/ 1000; + final totalNanoseconds = + seconds.toBigInt() * BigInt.from(_nanosecondsPerSecond) + + BigInt.from(nanoseconds); + final microseconds = (totalNanoseconds ~/ BigInt.from(1000)).toInt(); final value = Duration(microseconds: microseconds); if (nanoseconds.remainder(1000) != 0) { _exactDurationWire[value] = _ExactDurationWire( - seconds, + seconds.toInt(), nanoseconds, microseconds, ); @@ -101,7 +104,7 @@ int timestampWireNanoseconds(Timestamp value) { return value.nanoseconds; } -int dateTimeWireSeconds(DateTime value) { +Int64 dateTimeWireSeconds(DateTime value) { final utcValue = value.toUtc(); final microseconds = utcValue.microsecondsSinceEpoch; var seconds = microseconds ~/ Duration.microsecondsPerSecond; @@ -110,7 +113,7 @@ int dateTimeWireSeconds(DateTime value) { micros += Duration.microsecondsPerSecond; seconds -= 1; } - return seconds; + return Int64(seconds); } int dateTimeWireNanoseconds(DateTime value) { @@ -123,15 +126,18 @@ int dateTimeWireNanoseconds(DateTime value) { return micros * 1000; } -Timestamp timestampFromWire(int seconds, int nanoseconds) { +Timestamp timestampFromWire(Int64 seconds, int nanoseconds) { _validateTimestampNanoseconds(nanoseconds); return Timestamp(seconds, nanoseconds); } -DateTime dateTimeFromWire(int seconds, int nanoseconds) { +DateTime dateTimeFromWire(Int64 seconds, int nanoseconds) { _validateDateTimeNanoseconds(nanoseconds); + final microseconds = seconds.toBigInt() * + BigInt.from(Duration.microsecondsPerSecond) + + BigInt.from(nanoseconds ~/ 1000); return DateTime.fromMicrosecondsSinceEpoch( - seconds * Duration.microsecondsPerSecond + nanoseconds ~/ 1000, + microseconds.toInt(), isUtc: true, ); } @@ -144,12 +150,12 @@ final class LocalDateSerializer extends Serializer { @override void write(WriteContext context, LocalDate value) { - context.buffer.writeVarInt64(value.toEpochDay()); + context.buffer.writeVarInt64(Int64(value.toEpochDay())); } @override LocalDate read(ReadContext context) { - return LocalDate.fromEpochDay(context.buffer.readVarInt64()); + return LocalDate.fromEpochDay(context.buffer.readVarInt64().toInt()); } } diff --git a/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart b/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart index c439a86361..221941ba2e 100644 --- a/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/typed_array_serializers.dart @@ -17,7 +17,7 @@ * under the License. */ -import 'dart:typed_data'; +import 'dart:typed_data' as td; import 'package:fory/src/context/read_context.dart'; import 'package:fory/src/context/write_context.dart'; @@ -25,13 +25,19 @@ import 'package:fory/src/meta/type_ids.dart'; import 'package:fory/src/serializer/serializer.dart'; import 'package:fory/src/types/bfloat16.dart'; import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; void writeTypedArrayBytes( WriteContext context, Object values, ) { final bytes = switch (values) { - TypedData typed => typed.buffer.asUint8List( + Int64List typed => typed.buffer.asUint8List( + typed.offsetInBytes, + typed.lengthInBytes, + ), + Uint64List typed => typed.buffer.asUint8List( typed.offsetInBytes, typed.lengthInBytes, ), @@ -43,6 +49,10 @@ void writeTypedArrayBytes( typed.offsetInBytes, typed.lengthInBytes, ), + td.TypedData typed => typed.buffer.asUint8List( + typed.offsetInBytes, + typed.lengthInBytes, + ), _ => throw ArgumentError.value( values, 'values', @@ -56,7 +66,7 @@ void writeTypedArrayBytes( T readTypedArrayBytes( ReadContext context, int elementSize, - T Function(Uint8List bytes) viewBuilder, + T Function(td.Uint8List bytes) viewBuilder, ) { final byteSize = context.buffer.readVarUint32(); if (byteSize % elementSize != 0) { @@ -66,7 +76,7 @@ T readTypedArrayBytes( } var bytes = context.buffer.readBytes(byteSize); if (bytes.offsetInBytes % elementSize != 0) { - bytes = Uint8List.fromList(bytes); + bytes = td.Uint8List.fromList(bytes); } return viewBuilder(bytes); } @@ -99,7 +109,7 @@ final class BoolArraySerializer extends Serializer> { final class TypedArraySerializer extends Serializer { final int typeId; final int elementSize; - final T Function(Uint8List bytes) viewBuilder; + final T Function(td.Uint8List bytes) viewBuilder; const TypedArraySerializer( this.typeId, @@ -113,7 +123,7 @@ final class TypedArraySerializer extends Serializer { @override void write(WriteContext context, T value) { if (typeId == TypeIds.int8Array) { - final bytes = value as Int8List; + final bytes = value as td.Int8List; context.buffer.writeVarUint32(bytes.length); context.buffer.writeBytes(bytes); return; @@ -125,27 +135,27 @@ final class TypedArraySerializer extends Serializer { T read(ReadContext context) { if (typeId == TypeIds.int8Array) { final size = context.buffer.readVarUint32(); - return Int8List.fromList(context.buffer.readBytes(size)) as T; + return td.Int8List.fromList(context.buffer.readBytes(size)) as T; } return readTypedArrayBytes(context, elementSize, viewBuilder); } } const BoolArraySerializer boolArraySerializer = BoolArraySerializer(); -const TypedArraySerializer int8ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer int8ArraySerializer = + TypedArraySerializer( TypeIds.int8Array, 1, - Int8List.fromList, + td.Int8List.fromList, ); -const TypedArraySerializer int16ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer int16ArraySerializer = + TypedArraySerializer( TypeIds.int16Array, 2, _asInt16List, ); -const TypedArraySerializer int32ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer int32ArraySerializer = + TypedArraySerializer( TypeIds.int32Array, 4, _asInt32List, @@ -156,14 +166,14 @@ const TypedArraySerializer int64ArraySerializer = 8, _asInt64List, ); -const TypedArraySerializer uint16ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer uint16ArraySerializer = + TypedArraySerializer( TypeIds.uint16Array, 2, _asUint16List, ); -const TypedArraySerializer uint32ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer uint32ArraySerializer = + TypedArraySerializer( TypeIds.uint32Array, 4, _asUint32List, @@ -186,64 +196,63 @@ const TypedArraySerializer bfloat16ArraySerializer = 2, _asBfloat16List, ); -const TypedArraySerializer float32ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer float32ArraySerializer = + TypedArraySerializer( TypeIds.float32Array, 4, _asFloat32List, ); -const TypedArraySerializer float64ArraySerializer = - TypedArraySerializer( +const TypedArraySerializer float64ArraySerializer = + TypedArraySerializer( TypeIds.float64Array, 8, _asFloat64List, ); -Int16List _asInt16List(Uint8List bytes) => bytes.buffer.asInt16List( +td.Int16List _asInt16List(td.Uint8List bytes) => bytes.buffer.asInt16List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 2, ); -Int32List _asInt32List(Uint8List bytes) => bytes.buffer.asInt32List( +td.Int32List _asInt32List(td.Uint8List bytes) => bytes.buffer.asInt32List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 4, ); -Int64List _asInt64List(Uint8List bytes) => bytes.buffer.asInt64List( - bytes.offsetInBytes, - bytes.lengthInBytes ~/ 8, - ); +Int64List _asInt64List(td.Uint8List bytes) => + Int64List.view(bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 8); -Uint16List _asUint16List(Uint8List bytes) => bytes.buffer.asUint16List( +td.Uint16List _asUint16List(td.Uint8List bytes) => bytes.buffer.asUint16List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 2, ); -Float16List _asFloat16List(Uint8List bytes) => Float16List.view( +Float16List _asFloat16List(td.Uint8List bytes) => Float16List.view( bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 2); -Bfloat16List _asBfloat16List(Uint8List bytes) => Bfloat16List.view( +Bfloat16List _asBfloat16List(td.Uint8List bytes) => Bfloat16List.view( bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 2, ); -Uint32List _asUint32List(Uint8List bytes) => bytes.buffer.asUint32List( +td.Uint32List _asUint32List(td.Uint8List bytes) => bytes.buffer.asUint32List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 4, ); -Uint64List _asUint64List(Uint8List bytes) => bytes.buffer.asUint64List( +Uint64List _asUint64List(td.Uint8List bytes) => Uint64List.view( + bytes.buffer, bytes.offsetInBytes, bytes.lengthInBytes ~/ 8, ); -Float32List _asFloat32List(Uint8List bytes) => bytes.buffer.asFloat32List( +td.Float32List _asFloat32List(td.Uint8List bytes) => bytes.buffer.asFloat32List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 4, ); -Float64List _asFloat64List(Uint8List bytes) => bytes.buffer.asFloat64List( +td.Float64List _asFloat64List(td.Uint8List bytes) => bytes.buffer.asFloat64List( bytes.offsetInBytes, bytes.lengthInBytes ~/ 8, ); diff --git a/dart/packages/fory/lib/src/types/bfloat16.dart b/dart/packages/fory/lib/src/types/bfloat16.dart index 66d152f852..c1267c7d42 100644 --- a/dart/packages/fory/lib/src/types/bfloat16.dart +++ b/dart/packages/fory/lib/src/types/bfloat16.dart @@ -20,6 +20,10 @@ import 'dart:collection'; import 'dart:typed_data'; +const int _bfloat16ImplicitMantissaBit = 0x0010000000000000; +const int _bfloat16RoundIncrement = 0x0000100000000000; +const int _bfloat16MantissaDivisor = 0x0000200000000000; + /// Brain floating-point wrapper used by the xlang type system. /// /// [Bfloat16] stores an IEEE 754 bfloat16 payload exactly. Constructing from a @@ -37,16 +41,17 @@ final class Bfloat16 implements Comparable { /// Converts [value] to the closest representable bfloat16 value. factory Bfloat16.fromDouble(double value) { final data = ByteData(8)..setFloat64(0, value, Endian.little); - final bits = data.getUint64(0, Endian.little); - final sign = (bits >> 63) & 0x1; - final exponent = (bits >> 52) & 0x7ff; - final mantissa = bits & 0x000fffffffffffff; + final low32 = data.getUint32(0, Endian.little); + final high32 = data.getUint32(4, Endian.little); + final sign = (high32 >>> 31) & 0x1; + final exponent = (high32 >>> 20) & 0x7ff; + final mantissa = ((high32 & 0x000fffff) * 0x100000000) + low32; if (exponent == 0x7ff) { if (mantissa == 0) { return Bfloat16.fromBits((sign << 15) | 0x7f80); } - var payload = (mantissa >> 45) & 0x7f; + var payload = (mantissa ~/ _bfloat16MantissaDivisor) & 0x7f; if (payload == 0) { payload = 0x40; } else { @@ -63,14 +68,15 @@ final class Bfloat16 implements Comparable { if (adjustedExponent < -7) { return Bfloat16.fromBits(sign << 15); } - final shifted = (mantissa | (1 << 52)) >> (46 - adjustedExponent); - final rounded = (shifted + 1) >> 1; + final shifted = (mantissa + _bfloat16ImplicitMantissaBit) ~/ + _pow2Int(46 - adjustedExponent); + final rounded = (shifted + 1) ~/ 2; return Bfloat16.fromBits((sign << 15) | rounded); } - var roundedMantissa = mantissa + 0x0000100000000000; + var roundedMantissa = mantissa + _bfloat16RoundIncrement; var roundedExponent = adjustedExponent; - if ((roundedMantissa & 0x0010000000000000) != 0) { + if (roundedMantissa >= _bfloat16ImplicitMantissaBit) { roundedMantissa = 0; roundedExponent += 1; if (roundedExponent >= 0xff) { @@ -78,7 +84,9 @@ final class Bfloat16 implements Comparable { } } return Bfloat16.fromBits( - (sign << 15) | (roundedExponent << 7) | (roundedMantissa >> 45), + (sign << 15) | + (roundedExponent << 7) | + (roundedMantissa ~/ _bfloat16MantissaDivisor), ); } @@ -193,6 +201,14 @@ final class Bfloat16 implements Comparable { String toString() => value.toString(); } +int _pow2Int(int exponent) { + var result = 1; + for (var index = 0; index < exponent; index += 1) { + result *= 2; + } + return result; +} + /// Fixed-length contiguous storage for [Bfloat16] values. /// /// [Bfloat16List] behaves like a typed-data-style list whose elements are diff --git a/dart/packages/fory/lib/src/types/float16.dart b/dart/packages/fory/lib/src/types/float16.dart index 7c3532a9ab..dcb514e76a 100644 --- a/dart/packages/fory/lib/src/types/float16.dart +++ b/dart/packages/fory/lib/src/types/float16.dart @@ -20,6 +20,10 @@ import 'dart:collection'; import 'dart:typed_data'; +const int _float16ImplicitMantissaBit = 0x0010000000000000; +const int _float16RoundIncrement = 0x0000020000000000; +const int _float16MantissaDivisor = 0x0000040000000000; + /// Half-precision floating-point wrapper used by the xlang type system. /// /// [Float16] stores an IEEE 754 binary16 payload exactly. Constructing from a @@ -41,10 +45,11 @@ final class Float16 implements Comparable { return const Float16.fromBits(0x7e00); } final data = ByteData(8)..setFloat64(0, value, Endian.little); - final bits = data.getUint64(0, Endian.little); - final sign = (bits >> 63) & 0x1; - final exponent = (bits >> 52) & 0x7ff; - final mantissa = bits & 0x000fffffffffffff; + final low32 = data.getUint32(0, Endian.little); + final high32 = data.getUint32(4, Endian.little); + final sign = (high32 >>> 31) & 0x1; + final exponent = (high32 >>> 20) & 0x7ff; + final mantissa = ((high32 & 0x000fffff) * 0x100000000) + low32; if (exponent == 0x7ff) { return Float16.fromBits((sign << 15) | 0x7c00); @@ -58,14 +63,15 @@ final class Float16 implements Comparable { if (adjustedExponent < -10) { return Float16.fromBits(sign << 15); } - final shifted = (mantissa | (1 << 52)) >> (43 - adjustedExponent); - final rounded = (shifted + 1) >> 1; + final shifted = (mantissa + _float16ImplicitMantissaBit) ~/ + _pow2Int(43 - adjustedExponent); + final rounded = (shifted + 1) ~/ 2; return Float16.fromBits((sign << 15) | rounded); } - var roundedMantissa = mantissa + 0x0000020000000000; + var roundedMantissa = mantissa + _float16RoundIncrement; var roundedExponent = adjustedExponent; - if ((roundedMantissa & 0x0010000000000000) != 0) { + if (roundedMantissa >= _float16ImplicitMantissaBit) { roundedMantissa = 0; roundedExponent += 1; if (roundedExponent >= 0x1f) { @@ -73,7 +79,9 @@ final class Float16 implements Comparable { } } return Float16.fromBits( - (sign << 15) | (roundedExponent << 10) | (roundedMantissa >> 42), + (sign << 15) | + (roundedExponent << 10) | + (roundedMantissa ~/ _float16MantissaDivisor), ); } @@ -207,6 +215,14 @@ final class Float16 implements Comparable { String toString() => value.toString(); } +int _pow2Int(int exponent) { + var result = 1; + for (var index = 0; index < exponent; index += 1) { + result *= 2; + } + return result; +} + /// Fixed-length contiguous storage for [Float16] values. /// /// [Float16List] behaves like a typed-data-style list whose elements are diff --git a/dart/packages/fory/lib/src/types/int64.dart b/dart/packages/fory/lib/src/types/int64.dart new file mode 100644 index 0000000000..08b3aab19b --- /dev/null +++ b/dart/packages/fory/lib/src/types/int64.dart @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export 'int64_native.dart' + if (dart.library.js_interop) 'int64_web.dart'; diff --git a/dart/packages/fory/lib/src/types/int64_native.dart b/dart/packages/fory/lib/src/types/int64_native.dart new file mode 100644 index 0000000000..4decbb9cdf --- /dev/null +++ b/dart/packages/fory/lib/src/types/int64_native.dart @@ -0,0 +1,261 @@ +/* + * 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 'dart:typed_data' as td; + +/// Signed 64-bit integer wrapper used by the xlang type system. +extension type Int64._(int _value) implements int { + /// Creates a signed 64-bit value by truncating [value] to 64 bits. + factory Int64(int value) => Int64._(value.toSigned(64)); + + /// Creates a signed 64-bit value by truncating [value] to 64 bits. + factory Int64.fromBigInt(BigInt value) => Int64._(_normalizeSigned64(value)); + + /// Creates a signed 64-bit value from little-endian 32-bit words. + factory Int64.fromWords(int low32, int high32) => + Int64._(((high32 & 0xffffffff) << 32 | (low32 & 0xffffffff)) + .toSigned(64)); + + /// Parses a hexadecimal two's-complement payload. + factory Int64.parseHex(String value) => + Int64.fromBigInt(BigInt.parse(value, radix: 16)); + + /// The normalized signed 64-bit value on native platforms. + int get value => _value; + + /// Returns whether this value is negative. + bool get isNegative => _value < 0; + + /// Returns whether this value is zero. + bool get isZero => _value == 0; + + /// Returns the low 32 bits as an unsigned integer. + int get low32 => _value & 0xffffffff; + + /// Returns the high 32 bits as an unsigned integer. + int get high32Unsigned => (_value >> 32) & 0xffffffff; + + /// Returns the high 32 bits as a signed integer. + int get high32Signed => _value >> 32; + + /// Returns the exact value as a [BigInt]. + BigInt toBigInt() => BigInt.from(_value); + + /// Returns the exact native [int] value. + int toInt() => _value; + + int compareTo(Int64 other) => _value.compareTo(other._value); + + Int64 operator +(Object other) => switch (other) { + int otherValue => Int64(_value + otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator -(Object other) => switch (other) { + int otherValue => Int64(_value - otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator *(Object other) => switch (other) { + int otherValue => Int64(_value * otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator ~/(Object other) => switch (other) { + int otherValue => Int64(_value ~/ otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator %(Object other) => switch (other) { + int otherValue => Int64(_value % otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + double operator /(Object other) => switch (other) { + int otherValue => _value / otherValue, + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator -() => Int64(-_value); + + Int64 operator ~() => Int64(~_value); + + Int64 operator &(Object other) => switch (other) { + int otherValue => Int64(_value & otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator |(Object other) => switch (other) { + int otherValue => Int64(_value | otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator ^(Object other) => switch (other) { + int otherValue => Int64(_value ^ otherValue), + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + Int64 operator <<(int shift) => Int64(_value << shift); + + Int64 operator >>(int shift) => Int64(_value >> shift); + + bool operator <(Object other) => switch (other) { + int otherValue => _value < otherValue, + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + bool operator <=(Object other) => switch (other) { + int otherValue => _value <= otherValue, + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + bool operator >(Object other) => switch (other) { + int otherValue => _value > otherValue, + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; + + bool operator >=(Object other) => switch (other) { + int otherValue => _value >= otherValue, + _ => throw ArgumentError.value( + other, + 'other', + 'Expected an int or Int64.', + ), + }; +} + +/// Fixed-length contiguous storage for [Int64] values on native platforms. +extension type Int64List._(td.Int64List _storage) implements td.Int64List { + /// The number of bytes used by one [Int64] element. + static const int bytesPerElement = td.Int64List.bytesPerElement; + + /// Creates a zero-initialized list with [length] signed 64-bit elements. + Int64List(int length) : _storage = td.Int64List(length); + + /// Copies [values] into a new contiguous signed 64-bit list. + factory Int64List.fromList(Iterable values) { + final copied = values.toList(growable: false); + final storage = td.Int64List(copied.length); + for (var index = 0; index < copied.length; index += 1) { + storage[index] = switch (copied[index]) { + int value => Int64(value).value, + _ => throw ArgumentError.value( + copied[index], + 'values[$index]', + 'Expected an int or Int64.', + ), + }; + } + return Int64List._(storage); + } + + /// Creates a zero-copy view over [buffer]. + factory Int64List.view( + td.ByteBuffer buffer, [ + int offsetInBytes = 0, + int? length, + ]) { + return Int64List._(td.Int64List.view(buffer, offsetInBytes, length)); + } + + /// Creates a zero-copy element-range view of [data]. + factory Int64List.sublistView(td.TypedData data, [int start = 0, int? end]) { + return Int64List._(td.Int64List.sublistView(data, start, end)); + } + + /// The number of elements in this list. + int get length => _storage.length; + + /// Returns the element at [index]. + Int64 operator [](int index) => Int64(_storage[index]); + + /// Stores [value] at [index]. + void operator []=(int index, Int64 value) { + _storage[index] = value.value; + } + + td.ByteBuffer get buffer => _storage.buffer; + + int get elementSizeInBytes => _storage.elementSizeInBytes; + + int get offsetInBytes => _storage.offsetInBytes; + + int get lengthInBytes => _storage.lengthInBytes; + + Iterator get iterator => + Iterable.generate(length, (index) => this[index]).iterator; +} + +int _normalizeSigned64(BigInt value) { + final mask64 = (BigInt.one << 64) - BigInt.one; + final signBit64 = BigInt.one << 63; + var normalized = value & mask64; + if ((normalized & signBit64) != BigInt.zero) { + normalized -= BigInt.one << 64; + } + return normalized.toInt(); +} diff --git a/dart/packages/fory/lib/src/types/int64_web.dart b/dart/packages/fory/lib/src/types/int64_web.dart new file mode 100644 index 0000000000..b50a5493bc --- /dev/null +++ b/dart/packages/fory/lib/src/types/int64_web.dart @@ -0,0 +1,468 @@ +/* + * 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 'dart:collection'; +import 'dart:typed_data' as td; + +const int _int64SegmentBits = 22; +const int _int64SegmentMask = (1 << _int64SegmentBits) - 1; +const int _int64SegmentBase = 1 << _int64SegmentBits; +const int _int64HighBits = 20; +const int _int64HighMask = (1 << _int64HighBits) - 1; +const int _int64HighBase = 1 << _int64HighBits; +const int _int64HighSignBit = 1 << (_int64HighBits - 1); +const int _int64WordBase = 0x100000000; +final BigInt _int64Mask64Big = (BigInt.one << 64) - BigInt.one; +final BigInt _int64Mask32Big = (BigInt.one << 32) - BigInt.one; +final BigInt _int64SignBit64Big = BigInt.one << 63; +final BigInt _int64SafeMinBig = BigInt.from(-9007199254740991); +final BigInt _int64SafeMaxBig = BigInt.from(9007199254740991); + +/// Signed 64-bit integer wrapper used by the xlang type system on web builds. +final class Int64 implements Comparable { + final int _l; + final int _m; + final int _h; + + const Int64._parts(this._l, this._m, this._h); + + /// Creates a signed 64-bit value by truncating [value] to 64 bits. + factory Int64(int value) { + final low32 = value.toUnsigned(32); + final high32 = (value - low32) ~/ _int64WordBase; + return Int64.fromWords(low32, high32); + } + + /// Creates a signed 64-bit value by truncating [value] to 64 bits. + factory Int64.fromBigInt(BigInt value) { + var normalized = value & _int64Mask64Big; + if ((normalized & _int64SignBit64Big) != BigInt.zero) { + normalized -= BigInt.one << 64; + } + final unsigned = + normalized.isNegative ? normalized + (BigInt.one << 64) : normalized; + return Int64.fromWords( + (unsigned & _int64Mask32Big).toInt(), + ((unsigned >> 32) & _int64Mask32Big).toInt(), + ); + } + + /// Creates a signed 64-bit value from little-endian 32-bit words. + factory Int64.fromWords(int low32, int high32) { + final normalizedLow32 = low32.toUnsigned(32); + final normalizedHigh32 = high32.toSigned(32); + return Int64._parts( + normalizedLow32 & _int64SegmentMask, + ((normalizedLow32 >>> _int64SegmentBits) | + ((normalizedHigh32 & 0xfff) << 10)) & + _int64SegmentMask, + _normalizeInt64High(normalizedHigh32 >> 12), + ); + } + + /// Parses a hexadecimal two's-complement payload. + factory Int64.parseHex(String value) => + Int64.fromBigInt(BigInt.parse(value, radix: 16)); + + /// Returns whether this value is negative. + bool get isNegative => _h < 0; + + /// Returns whether this value is zero. + bool get isZero => _l == 0 && _m == 0 && _h == 0; + + /// Returns the low 32 bits as an unsigned integer. + int get low32 => _l | ((_m & 0x3ff) << 22); + + /// Returns the high 32 bits as an unsigned integer. + int get high32Unsigned => + ((_m >>> 10) & 0xfff) | ((_h & _int64HighMask) << 12); + + /// Returns the high 32 bits as a signed integer. + int get high32Signed => high32Unsigned.toSigned(32); + + /// Returns the exact value as a [BigInt]. + BigInt toBigInt() { + final unsigned = (BigInt.from(high32Unsigned) << 32) | BigInt.from(low32); + return isNegative ? unsigned - (BigInt.one << 64) : unsigned; + } + + /// Returns this value as a Dart [int] when it is JS-safe. + int toInt() { + final exact = toBigInt(); + if (exact < _int64SafeMinBig || exact > _int64SafeMaxBig) { + throw StateError('Int64 value $exact is not a JS-safe int.'); + } + return exact.toInt(); + } + + @override + int compareTo(Int64 other) { + if (_h != other._h) { + return _h < other._h ? -1 : 1; + } + if (_m != other._m) { + return _m < other._m ? -1 : 1; + } + if (_l != other._l) { + return _l < other._l ? -1 : 1; + } + return 0; + } + + Int64 operator +(Object other) => switch (other) { + int otherValue => _add(Int64(otherValue)), + Int64 otherValue => _add(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator -(Object other) => switch (other) { + int otherValue => _subtract(Int64(otherValue)), + Int64 otherValue => _subtract(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator *(Object other) => switch (other) { + int otherValue => + Int64.fromBigInt(toBigInt() * BigInt.from(otherValue)), + Int64 otherValue => + Int64.fromBigInt(toBigInt() * otherValue.toBigInt()), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator ~/(Object other) => switch (other) { + int otherValue => + Int64.fromBigInt(toBigInt() ~/ BigInt.from(otherValue)), + Int64 otherValue => + Int64.fromBigInt(toBigInt() ~/ otherValue.toBigInt()), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator %(Object other) => switch (other) { + int otherValue => + Int64.fromBigInt(toBigInt().remainder(BigInt.from(otherValue))), + Int64 otherValue => + Int64.fromBigInt(toBigInt().remainder(otherValue.toBigInt())), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + double operator /(Object other) => switch (other) { + int otherValue => toBigInt().toDouble() / otherValue, + Int64 otherValue => + toBigInt().toDouble() / otherValue.toBigInt().toDouble(), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator -() => Int64(0) - this; + + Int64 operator ~() => Int64._parts( + _l ^ _int64SegmentMask, _m ^ _int64SegmentMask, _normalizeInt64High(~_h)); + + Int64 operator &(Object other) => switch (other) { + int otherValue => _bitwiseAnd(Int64(otherValue)), + Int64 otherValue => _bitwiseAnd(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator |(Object other) => switch (other) { + int otherValue => _bitwiseOr(Int64(otherValue)), + Int64 otherValue => _bitwiseOr(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator ^(Object other) => switch (other) { + int otherValue => _bitwiseXor(Int64(otherValue)), + Int64 otherValue => _bitwiseXor(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + Int64 operator <<(int shift) { + RangeError.checkNotNegative(shift, 'shift'); + if (shift == 0) { + return this; + } + if (shift >= 64) { + return Int64(0); + } + final highBits = _h & _int64HighMask; + if (shift < 22) { + return Int64._parts( + (_l << shift) & _int64SegmentMask, + ((_m << shift) | (_l >>> (22 - shift))) & _int64SegmentMask, + _normalizeInt64High((highBits << shift) | (_m >>> (22 - shift))), + ); + } + if (shift < 44) { + final segmentShift = shift - 22; + return Int64._parts( + 0, + (_l << segmentShift) & _int64SegmentMask, + _normalizeInt64High( + (_m << segmentShift) | (_l >>> (22 - segmentShift)), + ), + ); + } + final segmentShift = shift - 44; + return Int64._parts( + 0, + 0, + _normalizeInt64High(_l << segmentShift), + ); + } + + Int64 operator >>(int shift) { + RangeError.checkNotNegative(shift, 'shift'); + if (shift == 0) { + return this; + } + if (shift >= 64) { + return _h < 0 ? Int64(-1) : Int64(0); + } + if (shift < 22) { + return Int64._parts( + ((_l >>> shift) | ((_m & _int64LowBitsMask(shift)) << (22 - shift))) & + _int64SegmentMask, + ((_m >>> shift) | + (((_h & _int64HighMask) & _int64LowBitsMask(shift)) << + (22 - shift))) & + _int64SegmentMask, + _normalizeInt64High(_h >> shift), + ); + } + if (shift < 44) { + final segmentShift = shift - 22; + return Int64._parts( + ((_m >>> segmentShift) | + ((_h & _int64LowBitsMask(segmentShift)) << + (22 - segmentShift))) & + _int64SegmentMask, + (_h >> segmentShift) & _int64SegmentMask, + _h < 0 ? -1 : 0, + ); + } + final segmentShift = shift - 44; + return Int64._parts( + (_h >> segmentShift) & _int64SegmentMask, + _h < 0 ? _int64SegmentMask : 0, + _h < 0 ? -1 : 0, + ); + } + + bool operator <(Object other) => switch (other) { + int otherValue => compareTo(Int64(otherValue)) < 0, + Int64 otherValue => compareTo(otherValue) < 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + bool operator <=(Object other) => switch (other) { + int otherValue => compareTo(Int64(otherValue)) <= 0, + Int64 otherValue => compareTo(otherValue) <= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + bool operator >(Object other) => switch (other) { + int otherValue => compareTo(Int64(otherValue)) > 0, + Int64 otherValue => compareTo(otherValue) > 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + bool operator >=(Object other) => switch (other) { + int otherValue => compareTo(Int64(otherValue)) >= 0, + Int64 otherValue => compareTo(otherValue) >= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Int64.'), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Int64 && other._l == _l && other._m == _m && other._h == _h; + + @override + int get hashCode => Object.hash(low32, high32Signed); + + @override + String toString() => toBigInt().toString(); + + Int64 _add(Int64 other) { + var low = _l + other._l; + var carry = low >> 22; + low &= _int64SegmentMask; + var mid = _m + other._m + carry; + carry = mid >> 22; + mid &= _int64SegmentMask; + return Int64._parts(low, mid, _normalizeInt64High(_h + other._h + carry)); + } + + Int64 _subtract(Int64 other) { + var low = _l - other._l; + var borrow = 0; + if (low < 0) { + low += _int64SegmentBase; + borrow = 1; + } + var mid = _m - other._m - borrow; + borrow = 0; + if (mid < 0) { + mid += _int64SegmentBase; + borrow = 1; + } + return Int64._parts( + low, + mid, + _normalizeInt64High(_h - other._h - borrow), + ); + } + + Int64 _bitwiseAnd(Int64 other) => Int64._parts( + _l & other._l, + _m & other._m, + _normalizeInt64High(_h & other._h), + ); + + Int64 _bitwiseOr(Int64 other) => Int64._parts( + _l | other._l, + _m | other._m, + _normalizeInt64High(_h | other._h), + ); + + Int64 _bitwiseXor(Int64 other) => Int64._parts( + _l ^ other._l, + _m ^ other._m, + _normalizeInt64High(_h ^ other._h), + ); +} + +/// Fixed-length contiguous storage for [Int64] values on web builds. +final class Int64List extends IterableBase { + /// The number of bytes used by one [Int64] element. + static const int bytesPerElement = 8; + + final td.Uint8List _bytes; + late final td.ByteData _view = td.ByteData.sublistView(_bytes); + + /// Creates a zero-initialized list with [length] signed 64-bit elements. + Int64List(int length) : _bytes = td.Uint8List(length * bytesPerElement); + + Int64List._(this._bytes); + + /// Copies [values] into a new contiguous signed 64-bit list. + factory Int64List.fromList(Iterable values) { + final copied = values.toList(growable: false); + final result = Int64List(copied.length); + for (var index = 0; index < copied.length; index += 1) { + result[index] = switch (copied[index]) { + int value => Int64(value), + Int64 value => value, + _ => throw ArgumentError.value( + copied[index], + 'values[$index]', + 'Expected an int or Int64.', + ), + }; + } + return result; + } + + /// Creates a zero-copy view over [buffer]. + factory Int64List.view( + td.ByteBuffer buffer, [ + int offsetInBytes = 0, + int? length, + ]) { + final byteLength = length == null + ? buffer.lengthInBytes - offsetInBytes + : length * bytesPerElement; + return Int64List._(td.Uint8List.view(buffer, offsetInBytes, byteLength)); + } + + /// Creates a zero-copy element-range view of [data]. + factory Int64List.sublistView(td.TypedData data, [int start = 0, int? end]) { + final totalLength = data.lengthInBytes ~/ bytesPerElement; + final endIndex = RangeError.checkValidRange(start, end, totalLength); + return Int64List.view( + data.buffer, + data.offsetInBytes + start * bytesPerElement, + endIndex - start, + ); + } + + /// The number of elements in this list. + @override + int get length => _bytes.lengthInBytes ~/ bytesPerElement; + + /// Returns the element at [index]. + Int64 operator [](int index) { + RangeError.checkValidIndex(index, this, 'index', length); + final byteOffset = index * bytesPerElement; + return Int64.fromWords( + _view.getUint32(byteOffset, td.Endian.little), + _view.getInt32(byteOffset + 4, td.Endian.little), + ); + } + + /// Stores [value] at [index]. + void operator []=(int index, Int64 value) { + RangeError.checkValidIndex(index, this, 'index', length); + final byteOffset = index * bytesPerElement; + _view.setUint32(byteOffset, value.low32, td.Endian.little); + _view.setUint32(byteOffset + 4, value.high32Unsigned, td.Endian.little); + } + + td.ByteBuffer get buffer => _bytes.buffer; + + int get elementSizeInBytes => bytesPerElement; + + int get offsetInBytes => _bytes.offsetInBytes; + + int get lengthInBytes => _bytes.lengthInBytes; + + @override + Iterator get iterator => + Iterable.generate(length, (index) => this[index]).iterator; + + @override + String toString() => toList().toString(); +} + +int _normalizeInt64High(int value) { + final normalized = value & _int64HighMask; + if ((normalized & _int64HighSignBit) != 0) { + return normalized - _int64HighBase; + } + return normalized; +} + +int _int64LowBitsMask(int bits) { + if (bits <= 0) { + return 0; + } + return (1 << bits) - 1; +} diff --git a/dart/packages/fory/lib/src/types/timestamp.dart b/dart/packages/fory/lib/src/types/timestamp.dart index 060b529f29..08cc4f8275 100644 --- a/dart/packages/fory/lib/src/types/timestamp.dart +++ b/dart/packages/fory/lib/src/types/timestamp.dart @@ -17,16 +17,18 @@ * under the License. */ +import 'package:fory/src/types/int64.dart'; + /// Timestamp with second and nanosecond precision in UTC. final class Timestamp implements Comparable { /// Whole seconds since the Unix epoch. - final int seconds; + final Int64 seconds; /// Nanoseconds within the second. final int nanoseconds; /// Creates a timestamp from epoch seconds and nanoseconds. - const Timestamp(this.seconds, this.nanoseconds); + Timestamp(this.seconds, this.nanoseconds); /// Converts a [DateTime] to UTC timestamp components. factory Timestamp.fromDateTime(DateTime value) { @@ -38,12 +40,14 @@ final class Timestamp implements Comparable { micros += Duration.microsecondsPerSecond; seconds -= 1; } - return Timestamp(seconds, micros * 1000); + return Timestamp(Int64(seconds), micros * 1000); } /// Converts this timestamp to a UTC [DateTime]. DateTime toDateTime() => DateTime.fromMicrosecondsSinceEpoch( - seconds * Duration.microsecondsPerSecond + nanoseconds ~/ 1000, + (seconds.toBigInt() * BigInt.from(Duration.microsecondsPerSecond) + + BigInt.from(nanoseconds ~/ 1000)) + .toInt(), isUtc: true, ); diff --git a/dart/packages/fory/lib/src/types/uint64.dart b/dart/packages/fory/lib/src/types/uint64.dart index 1c335e84f2..b3a9881c5d 100644 --- a/dart/packages/fory/lib/src/types/uint64.dart +++ b/dart/packages/fory/lib/src/types/uint64.dart @@ -17,131 +17,5 @@ * under the License. */ -/// Unsigned 64-bit integer wrapper used by the xlang type system. -/// -/// The constructor truncates [value] to 64 bits using unsigned wrap-around -/// semantics. This wrapper is the default Dart carrier for root and untyped -/// xlang `uint64`, `varuint64`, and `taggeduint64` payloads; declared `int` -/// fields annotated with `@Uint64Type(...)` still read back as plain Dart -/// [int] values through generated conversion paths. -final class Uint64 implements Comparable { - /// The normalized unsigned 64-bit value. - final int value; - - /// Creates an unsigned 64-bit value by truncating [value] to 64 bits. - Uint64(int value) : value = value.toUnsigned(64); - - @override - int compareTo(Uint64 other) => value.compareTo(other.value); - - Uint64 operator +(Object other) => switch (other) { - int otherValue => Uint64(value + otherValue), - Uint64 otherValue => Uint64(value + otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator -(Object other) => switch (other) { - int otherValue => Uint64(value - otherValue), - Uint64 otherValue => Uint64(value - otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator *(Object other) => switch (other) { - int otherValue => Uint64(value * otherValue), - Uint64 otherValue => Uint64(value * otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator ~/(Object other) => switch (other) { - int otherValue => Uint64(value ~/ otherValue), - Uint64 otherValue => Uint64(value ~/ otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator %(Object other) => switch (other) { - int otherValue => Uint64(value % otherValue), - Uint64 otherValue => Uint64(value % otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - double operator /(Object other) => switch (other) { - int otherValue => value / otherValue, - Uint64 otherValue => value / otherValue.value, - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator -() => Uint64(-value); - - Uint64 operator ~() => Uint64(~value); - - Uint64 operator &(Object other) => switch (other) { - int otherValue => Uint64(value & otherValue), - Uint64 otherValue => Uint64(value & otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator |(Object other) => switch (other) { - int otherValue => Uint64(value | otherValue), - Uint64 otherValue => Uint64(value | otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator ^(Object other) => switch (other) { - int otherValue => Uint64(value ^ otherValue), - Uint64 otherValue => Uint64(value ^ otherValue.value), - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - Uint64 operator <<(int shift) => Uint64(value << shift); - - Uint64 operator >>(int shift) => Uint64(value >> shift); - - bool operator <(Object other) => switch (other) { - int otherValue => value < otherValue, - Uint64 otherValue => value < otherValue.value, - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - bool operator <=(Object other) => switch (other) { - int otherValue => value <= otherValue, - Uint64 otherValue => value <= otherValue.value, - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - bool operator >(Object other) => switch (other) { - int otherValue => value > otherValue, - Uint64 otherValue => value > otherValue.value, - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - bool operator >=(Object other) => switch (other) { - int otherValue => value >= otherValue, - Uint64 otherValue => value >= otherValue.value, - _ => throw ArgumentError.value( - other, 'other', 'Expected an int or Uint64.'), - }; - - int toInt() => value; - - @override - bool operator ==(Object other) => - identical(this, other) || other is Uint64 && other.value == value; - - @override - int get hashCode => value.hashCode; - - @override - String toString() => value.toString(); -} +export 'uint64_native.dart' + if (dart.library.js_interop) 'uint64_web.dart'; diff --git a/dart/packages/fory/lib/src/types/uint64_native.dart b/dart/packages/fory/lib/src/types/uint64_native.dart new file mode 100644 index 0000000000..09d2da11f2 --- /dev/null +++ b/dart/packages/fory/lib/src/types/uint64_native.dart @@ -0,0 +1,243 @@ +/* + * 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 'dart:typed_data' as td; + +final BigInt _uint64Mask64Big = (BigInt.one << 64) - BigInt.one; +final BigInt _uint64SignBit64Big = BigInt.one << 63; + +/// Unsigned 64-bit integer wrapper used by the xlang type system. +extension type Uint64._(int _value) implements int { + /// Creates an unsigned 64-bit value by truncating [value] to 64 bits. + factory Uint64(int value) => Uint64._(value.toSigned(64)); + + /// Creates an unsigned 64-bit value by truncating [value] to 64 bits. + factory Uint64.fromBigInt(BigInt value) => + Uint64._(_normalizeUnsigned64(value)); + + /// Creates an unsigned 64-bit value from little-endian 32-bit words. + factory Uint64.fromWords(int low32, int high32) { + final normalizedLow32 = low32 & 0xffffffff; + final normalizedHigh32 = high32 & 0xffffffff; + return Uint64._( + (((normalizedHigh32 << 32) | normalizedLow32).toSigned(64)), + ); + } + + /// Parses a hexadecimal payload. + factory Uint64.parseHex(String value) => + Uint64.fromBigInt(BigInt.parse(value, radix: 16)); + + /// The normalized 64-bit bit pattern stored in a native [int]. + int get value => _value; + + /// Returns whether this value is zero. + bool get isZero => _value == 0; + + /// Returns the low 32 bits as an unsigned integer. + int get low32 => _value & 0xffffffff; + + /// Returns the high 32 bits as an unsigned integer. + int get high32Unsigned => (_value >> 32) & 0xffffffff; + + /// Returns the exact value as a [BigInt]. + BigInt toBigInt() => _value >= 0 + ? BigInt.from(_value) + : BigInt.from(_value) + (BigInt.one << 64); + + /// Returns this value as a native [int] when it is exactly representable. + int toInt() { + if (_value < 0) { + final exact = toBigInt(); + throw StateError( + 'Uint64 value $exact is not representable as a native int.', + ); + } + return _value; + } + + int compareTo(Uint64 other) { + final highCompare = high32Unsigned.compareTo(other.high32Unsigned); + if (highCompare != 0) { + return highCompare; + } + return low32.compareTo(other.low32); + } + + Uint64 operator +(Object other) => switch (other) { + int otherValue => Uint64(_value + otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator -(Object other) => switch (other) { + int otherValue => Uint64(_value - otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator *(Object other) => switch (other) { + int otherValue => Uint64(_value * Uint64(otherValue).value), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator ~/(Object other) => switch (other) { + int otherValue => + Uint64.fromBigInt(toBigInt() ~/ Uint64(otherValue).toBigInt()), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator %(Object other) => switch (other) { + int otherValue => Uint64.fromBigInt( + toBigInt().remainder(Uint64(otherValue).toBigInt())), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + double operator /(Object other) => switch (other) { + int otherValue => + toBigInt().toDouble() / Uint64(otherValue).toBigInt().toDouble(), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator -() => Uint64(-_value); + + Uint64 operator ~() => Uint64(~_value); + + Uint64 operator &(Object other) => switch (other) { + int otherValue => Uint64(_value & otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator |(Object other) => switch (other) { + int otherValue => Uint64(_value | otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator ^(Object other) => switch (other) { + int otherValue => Uint64(_value ^ otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator <<(int shift) => Uint64(_value << shift); + + Uint64 operator >>(int shift) => Uint64(_value >>> shift); + + Uint64 operator >>>(int shift) => Uint64(_value >>> shift); + + bool operator <(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) < 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator <=(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) <= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator >(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) > 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator >=(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) >= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; +} + +/// Fixed-length contiguous storage for [Uint64] values on native platforms. +extension type Uint64List._(td.Uint64List _storage) implements td.Uint64List { + /// The number of bytes used by one [Uint64] element. + static const int bytesPerElement = td.Uint64List.bytesPerElement; + + /// Creates a zero-initialized list with [length] unsigned 64-bit elements. + Uint64List(int length) : _storage = td.Uint64List(length); + + /// Copies [values] into a new contiguous unsigned 64-bit list. + factory Uint64List.fromList(Iterable values) { + final copied = values.toList(growable: false); + final storage = td.Uint64List(copied.length); + for (var index = 0; index < copied.length; index += 1) { + storage[index] = switch (copied[index]) { + int value => Uint64(value).value, + _ => throw ArgumentError.value( + copied[index], + 'values[$index]', + 'Expected an int or Uint64.', + ), + }; + } + return Uint64List._(storage); + } + + /// Creates a zero-copy view over [buffer]. + factory Uint64List.view( + td.ByteBuffer buffer, [ + int offsetInBytes = 0, + int? length, + ]) { + return Uint64List._(td.Uint64List.view(buffer, offsetInBytes, length)); + } + + /// Creates a zero-copy element-range view of [data]. + factory Uint64List.sublistView(td.TypedData data, [int start = 0, int? end]) { + return Uint64List._(td.Uint64List.sublistView(data, start, end)); + } + + /// The number of elements in this list. + int get length => _storage.length; + + /// Returns the element at [index]. + Uint64 operator [](int index) => Uint64(_storage[index]); + + /// Stores [value] at [index]. + void operator []=(int index, Uint64 value) { + _storage[index] = value.value; + } + + td.ByteBuffer get buffer => _storage.buffer; + + int get elementSizeInBytes => _storage.elementSizeInBytes; + + int get offsetInBytes => _storage.offsetInBytes; + + int get lengthInBytes => _storage.lengthInBytes; + + Iterator get iterator => + Iterable.generate(length, (index) => this[index]).iterator; +} + +int _normalizeUnsigned64(BigInt value) { + var normalized = value & _uint64Mask64Big; + if ((normalized & _uint64SignBit64Big) != BigInt.zero) { + normalized -= BigInt.one << 64; + } + return normalized.toInt(); +} diff --git a/dart/packages/fory/lib/src/types/uint64_web.dart b/dart/packages/fory/lib/src/types/uint64_web.dart new file mode 100644 index 0000000000..f3bed35f88 --- /dev/null +++ b/dart/packages/fory/lib/src/types/uint64_web.dart @@ -0,0 +1,430 @@ +/* + * 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 'dart:collection'; +import 'dart:typed_data' as td; + +const int _uint64SegmentBits = 22; +const int _uint64SegmentMask = (1 << _uint64SegmentBits) - 1; +const int _uint64SegmentBase = 1 << _uint64SegmentBits; +const int _uint64HighBits = 20; +const int _uint64HighMask = (1 << _uint64HighBits) - 1; +const int _uint64WordBase = 0x100000000; +final BigInt _uint64Mask64Big = (BigInt.one << 64) - BigInt.one; +final BigInt _uint64Mask32Big = (BigInt.one << 32) - BigInt.one; +final BigInt _uint64SafeMaxBig = BigInt.from(9007199254740991); + +/// Unsigned 64-bit integer wrapper used by the xlang type system on web builds. +final class Uint64 implements Comparable { + final int _l; + final int _m; + final int _h; + + const Uint64._parts(this._l, this._m, this._h); + + /// Creates an unsigned 64-bit value by truncating [value] to 64 bits. + factory Uint64(int value) { + final low32 = value.toUnsigned(32); + final high32 = (value - low32) ~/ _uint64WordBase; + return Uint64.fromWords(low32, high32); + } + + /// Creates an unsigned 64-bit value by truncating [value] to 64 bits. + factory Uint64.fromBigInt(BigInt value) { + final normalized = value & _uint64Mask64Big; + return Uint64.fromWords( + (normalized & _uint64Mask32Big).toInt(), + ((normalized >> 32) & _uint64Mask32Big).toInt(), + ); + } + + /// Creates an unsigned 64-bit value from little-endian 32-bit words. + factory Uint64.fromWords(int low32, int high32) { + final normalizedLow32 = low32.toUnsigned(32); + final normalizedHigh32 = high32.toUnsigned(32); + return Uint64._parts( + normalizedLow32 & _uint64SegmentMask, + ((normalizedLow32 >>> _uint64SegmentBits) | + ((normalizedHigh32 & 0xfff) << 10)) & + _uint64SegmentMask, + (normalizedHigh32 >>> 12) & _uint64HighMask, + ); + } + + /// Parses a hexadecimal payload. + factory Uint64.parseHex(String value) => + Uint64.fromBigInt(BigInt.parse(value, radix: 16)); + + /// Returns whether this value is zero. + bool get isZero => _l == 0 && _m == 0 && _h == 0; + + /// Returns the low 32 bits as an unsigned integer. + int get low32 => _l | ((_m & 0x3ff) << 22); + + /// Returns the high 32 bits as an unsigned integer. + int get high32Unsigned => ((_m >>> 10) & 0xfff) | (_h << 12); + + /// Returns the exact value as a [BigInt]. + BigInt toBigInt() => (BigInt.from(high32Unsigned) << 32) | BigInt.from(low32); + + /// Returns this value as a Dart [int] when it is JS-safe. + int toInt() { + final exact = toBigInt(); + if (exact > _uint64SafeMaxBig) { + throw StateError('Uint64 value $exact is not a JS-safe int.'); + } + return exact.toInt(); + } + + @override + int compareTo(Uint64 other) { + if (_h != other._h) { + return _h < other._h ? -1 : 1; + } + if (_m != other._m) { + return _m < other._m ? -1 : 1; + } + if (_l != other._l) { + return _l < other._l ? -1 : 1; + } + return 0; + } + + Uint64 operator +(Object other) => switch (other) { + int otherValue => _add(Uint64(otherValue)), + Uint64 otherValue => _add(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator -(Object other) => switch (other) { + int otherValue => _subtract(Uint64(otherValue)), + Uint64 otherValue => _subtract(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator *(Object other) => switch (other) { + int otherValue => + Uint64.fromBigInt(toBigInt() * BigInt.from(otherValue)), + Uint64 otherValue => + Uint64.fromBigInt(toBigInt() * otherValue.toBigInt()), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator ~/(Object other) => switch (other) { + int otherValue => + Uint64.fromBigInt(toBigInt() ~/ BigInt.from(otherValue)), + Uint64 otherValue => + Uint64.fromBigInt(toBigInt() ~/ otherValue.toBigInt()), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator %(Object other) => switch (other) { + int otherValue => + Uint64.fromBigInt(toBigInt().remainder(BigInt.from(otherValue))), + Uint64 otherValue => + Uint64.fromBigInt(toBigInt().remainder(otherValue.toBigInt())), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + double operator /(Object other) => switch (other) { + int otherValue => toBigInt().toDouble() / otherValue, + Uint64 otherValue => + toBigInt().toDouble() / otherValue.toBigInt().toDouble(), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator -() => Uint64(0) - this; + + Uint64 operator ~() => Uint64._parts( + _l ^ _uint64SegmentMask, + _m ^ _uint64SegmentMask, + _h ^ _uint64HighMask, + ); + + Uint64 operator &(Object other) => switch (other) { + int otherValue => _bitwiseAnd(Uint64(otherValue)), + Uint64 otherValue => _bitwiseAnd(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator |(Object other) => switch (other) { + int otherValue => _bitwiseOr(Uint64(otherValue)), + Uint64 otherValue => _bitwiseOr(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator ^(Object other) => switch (other) { + int otherValue => _bitwiseXor(Uint64(otherValue)), + Uint64 otherValue => _bitwiseXor(otherValue), + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + Uint64 operator <<(int shift) { + RangeError.checkNotNegative(shift, 'shift'); + if (shift == 0) { + return this; + } + if (shift >= 64) { + return Uint64(0); + } + if (shift < 22) { + return Uint64._parts( + (_l << shift) & _uint64SegmentMask, + ((_m << shift) | (_l >>> (22 - shift))) & _uint64SegmentMask, + (((_h << shift) | (_m >>> (22 - shift))) & _uint64HighMask), + ); + } + if (shift < 44) { + final segmentShift = shift - 22; + return Uint64._parts( + 0, + (_l << segmentShift) & _uint64SegmentMask, + (((_m << segmentShift) | (_l >>> (22 - segmentShift))) & + _uint64HighMask), + ); + } + final segmentShift = shift - 44; + return Uint64._parts(0, 0, (_l << segmentShift) & _uint64HighMask); + } + + Uint64 operator >>(int shift) { + RangeError.checkNotNegative(shift, 'shift'); + if (shift == 0) { + return this; + } + if (shift >= 64) { + return Uint64(0); + } + if (shift < 22) { + return Uint64._parts( + ((_l >>> shift) | ((_m & _uint64LowBitsMask(shift)) << (22 - shift))) & + _uint64SegmentMask, + ((_m >>> shift) | ((_h & _uint64LowBitsMask(shift)) << (22 - shift))) & + _uint64SegmentMask, + _h >>> shift, + ); + } + if (shift < 44) { + final segmentShift = shift - 22; + return Uint64._parts( + ((_m >>> segmentShift) | + ((_h & _uint64LowBitsMask(segmentShift)) << + (22 - segmentShift))) & + _uint64SegmentMask, + _h >>> segmentShift, + 0, + ); + } + final segmentShift = shift - 44; + return Uint64._parts(_h >>> segmentShift, 0, 0); + } + + Uint64 operator >>>(int shift) => this >> shift; + + bool operator <(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) < 0, + Uint64 otherValue => compareTo(otherValue) < 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator <=(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) <= 0, + Uint64 otherValue => compareTo(otherValue) <= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator >(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) > 0, + Uint64 otherValue => compareTo(otherValue) > 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + bool operator >=(Object other) => switch (other) { + int otherValue => compareTo(Uint64(otherValue)) >= 0, + Uint64 otherValue => compareTo(otherValue) >= 0, + _ => throw ArgumentError.value( + other, 'other', 'Expected an int or Uint64.'), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Uint64 && other._l == _l && other._m == _m && other._h == _h; + + @override + int get hashCode => Object.hash(low32, high32Unsigned); + + @override + String toString() => toBigInt().toString(); + + Uint64 _add(Uint64 other) { + var low = _l + other._l; + var carry = low >> 22; + low &= _uint64SegmentMask; + var mid = _m + other._m + carry; + carry = mid >> 22; + mid &= _uint64SegmentMask; + return Uint64._parts(low, mid, (_h + other._h + carry) & _uint64HighMask); + } + + Uint64 _subtract(Uint64 other) { + var low = _l - other._l; + var borrow = 0; + if (low < 0) { + low += _uint64SegmentBase; + borrow = 1; + } + var mid = _m - other._m - borrow; + borrow = 0; + if (mid < 0) { + mid += _uint64SegmentBase; + borrow = 1; + } + return Uint64._parts(low, mid, (_h - other._h - borrow) & _uint64HighMask); + } + + Uint64 _bitwiseAnd(Uint64 other) => Uint64._parts( + _l & other._l, + _m & other._m, + _h & other._h, + ); + + Uint64 _bitwiseOr(Uint64 other) => Uint64._parts( + _l | other._l, + _m | other._m, + _h | other._h, + ); + + Uint64 _bitwiseXor(Uint64 other) => Uint64._parts( + _l ^ other._l, + _m ^ other._m, + _h ^ other._h, + ); +} + +/// Fixed-length contiguous storage for [Uint64] values on web builds. +final class Uint64List extends IterableBase { + /// The number of bytes used by one [Uint64] element. + static const int bytesPerElement = 8; + + final td.Uint8List _bytes; + late final td.ByteData _view = td.ByteData.sublistView(_bytes); + + /// Creates a zero-initialized list with [length] unsigned 64-bit elements. + Uint64List(int length) : _bytes = td.Uint8List(length * bytesPerElement); + + Uint64List._(this._bytes); + + /// Copies [values] into a new contiguous unsigned 64-bit list. + factory Uint64List.fromList(Iterable values) { + final copied = values.toList(growable: false); + final result = Uint64List(copied.length); + for (var index = 0; index < copied.length; index += 1) { + result[index] = switch (copied[index]) { + int value => Uint64(value), + Uint64 value => value, + _ => throw ArgumentError.value( + copied[index], + 'values[$index]', + 'Expected an int or Uint64.', + ), + }; + } + return result; + } + + /// Creates a zero-copy view over [buffer]. + factory Uint64List.view( + td.ByteBuffer buffer, [ + int offsetInBytes = 0, + int? length, + ]) { + final byteLength = length == null + ? buffer.lengthInBytes - offsetInBytes + : length * bytesPerElement; + return Uint64List._(td.Uint8List.view(buffer, offsetInBytes, byteLength)); + } + + /// Creates a zero-copy element-range view of [data]. + factory Uint64List.sublistView(td.TypedData data, [int start = 0, int? end]) { + final totalLength = data.lengthInBytes ~/ bytesPerElement; + final endIndex = RangeError.checkValidRange(start, end, totalLength); + return Uint64List.view( + data.buffer, + data.offsetInBytes + start * bytesPerElement, + endIndex - start, + ); + } + + /// The number of elements in this list. + @override + int get length => _bytes.lengthInBytes ~/ bytesPerElement; + + /// Returns the element at [index]. + Uint64 operator [](int index) { + RangeError.checkValidIndex(index, this, 'index', length); + final byteOffset = index * bytesPerElement; + return Uint64.fromWords( + _view.getUint32(byteOffset, td.Endian.little), + _view.getUint32(byteOffset + 4, td.Endian.little), + ); + } + + /// Stores [value] at [index]. + void operator []=(int index, Uint64 value) { + RangeError.checkValidIndex(index, this, 'index', length); + final byteOffset = index * bytesPerElement; + _view.setUint32(byteOffset, value.low32, td.Endian.little); + _view.setUint32(byteOffset + 4, value.high32Unsigned, td.Endian.little); + } + + td.ByteBuffer get buffer => _bytes.buffer; + + int get elementSizeInBytes => bytesPerElement; + + int get offsetInBytes => _bytes.offsetInBytes; + + int get lengthInBytes => _bytes.lengthInBytes; + + @override + Iterator get iterator => + Iterable.generate(length, (index) => this[index]).iterator; + + @override + String toString() => toList().toString(); +} + +int _uint64LowBitsMask(int bits) { + if (bits <= 0) { + return 0; + } + return (1 << bits) - 1; +} diff --git a/dart/packages/fory/lib/src/util/hash_util.dart b/dart/packages/fory/lib/src/util/hash_util.dart index b6c211bd5e..cc59013b2c 100644 --- a/dart/packages/fory/lib/src/util/hash_util.dart +++ b/dart/packages/fory/lib/src/util/hash_util.dart @@ -22,6 +22,7 @@ import 'dart:convert'; import 'package:fory/src/meta/field_info.dart'; import 'package:fory/src/meta/type_def.dart'; import 'package:fory/src/meta/type_ids.dart'; +import 'package:fory/src/types/int64.dart'; const int _typeDefCompressMetaFlag = 1 << 9; const int _typeDefHasFieldsMetaFlag = 1 << 8; @@ -136,16 +137,16 @@ final BigInt _c2Big = BigInt.parse('4cf5ad432745937f', radix: 16); return (h1, h2); } -int metaStringHash(List bytes, {int encoding = 0}) { +Int64 metaStringHash(List bytes, {int encoding = 0}) { var hash = _absSigned64(murmurHash3X64_128(bytes).$1); if (hash == 0) { hash += 0x100; } hash = (BigInt.from(hash) & _metaStringHashMaskBig).toInt(); - return hash | (encoding & 0xff); + return Int64(hash | (encoding & 0xff)); } -int typeDefHeader( +Int64 typeDefHeader( List bytes, { required bool hasFieldsMeta, bool compressed = false, @@ -160,7 +161,7 @@ int typeDefHeader( } header |= bytes.length > _typeDefMetaSizeMask ? _typeDefMetaSizeMask : bytes.length; - return _toSigned64(header); + return Int64(_toSigned64(header)); } int schemaHash(TypeDef typeDef) { diff --git a/dart/packages/fory/lib/src/util/int64_codec.dart b/dart/packages/fory/lib/src/util/int64_codec.dart new file mode 100644 index 0000000000..b31e0132f6 --- /dev/null +++ b/dart/packages/fory/lib/src/util/int64_codec.dart @@ -0,0 +1,84 @@ +/* + * 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 'dart:typed_data'; + +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; + +void writeInt64LittleEndian(ByteData view, int offset, Int64 value) { + view.setUint32(offset, value.low32, Endian.little); + view.setUint32(offset + 4, value.high32Unsigned, Endian.little); +} + +Int64 readInt64LittleEndian(ByteData view, int offset) { + return Int64.fromWords( + view.getUint32(offset, Endian.little), + view.getInt32(offset + 4, Endian.little), + ); +} + +void writeUint64LittleEndian(ByteData view, int offset, Uint64 value) { + view.setUint32(offset, value.low32, Endian.little); + view.setUint32(offset + 4, value.high32Unsigned, Endian.little); +} + +Uint64 readUint64LittleEndian(ByteData view, int offset) { + return Uint64.fromWords( + view.getUint32(offset, Endian.little), + view.getUint32(offset + 4, Endian.little), + ); +} + +Uint64 zigZagEncodeInt64(Int64 value) { + final encoded = (value << 1) ^ (value >> 63); + return Uint64.fromWords(encoded.low32, encoded.high32Unsigned); +} + +Int64 zigZagDecodeInt64(Uint64 encoded) { + final magnitude = encoded >> 1; + final decoded = Int64.fromWords(magnitude.low32, magnitude.high32Unsigned); + if ((encoded.low32 & 1) == 0) { + return decoded; + } + return -(decoded + 1); +} + +void writeVarUint64Bytes(Uint64 value, void Function(int byte) writeByte) { + var remaining = value; + for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { + writeByte((remaining.low32 & 0x7f) | 0x80); + remaining = remaining >> 7; + } + writeByte(remaining.toInt()); +} + +Uint64 readVarUint64Bytes(int Function() readByte) { + var shift = 0; + var result = Uint64(0); + while (shift < 56) { + final byte = readByte(); + result = result | (Uint64(byte & 0x7f) << shift); + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + return result | (Uint64(readByte()) << 56); +} diff --git a/dart/packages/fory/test/buffer_test.dart b/dart/packages/fory/test/buffer_test.dart index 4c972e5a9b..b42d14fb30 100644 --- a/dart/packages/fory/test/buffer_test.dart +++ b/dart/packages/fory/test/buffer_test.dart @@ -22,6 +22,14 @@ import 'dart:typed_data'; import 'package:fory/fory.dart'; import 'package:test/test.dart'; +Int64 _i64Hex(String value) => Int64.parseHex(value); + +Uint64 _u64Hex(String value) => Uint64.parseHex(value); + +Int64 _i64Pow2(int shift) => Int64(1) << shift; + +Uint64 _u64Pow2(int shift) => Uint64(1) << shift; + void main() { group('Buffer', () { test('round-trips fixed-width primitives, 16-bit floats, and bytes', () { @@ -37,8 +45,8 @@ void main() { buffer.writeUint16(0xffff); buffer.writeInt32(-0x80000000); buffer.writeUint32(0xffffffff); - buffer.writeInt64(-0x8000000000000000); - buffer.writeUint64(0xffffffffffffffff); + buffer.writeInt64(_i64Hex('8000000000000000')); + buffer.writeUint64(_u64Hex('ffffffffffffffff')); buffer.writeFloat16(half); buffer.writeBfloat16(brain); buffer.writeFloat32(1.5); @@ -53,8 +61,8 @@ void main() { expect(buffer.readUint16(), equals(0xffff)); expect(buffer.readInt32(), equals(-0x80000000)); expect(buffer.readUint32(), equals(0xffffffff)); - expect(buffer.readInt64(), equals(-0x8000000000000000)); - expect(buffer.readUint64(), equals(0xffffffffffffffff)); + expect(buffer.readInt64(), equals(_i64Hex('8000000000000000'))); + expect(buffer.readUint64(), equals(_u64Hex('ffffffffffffffff'))); expect(buffer.readFloat16(), equals(half)); expect(buffer.readBfloat16(), equals(brain)); expect(buffer.readFloat32(), closeTo(1.5, 0.0001)); @@ -190,33 +198,33 @@ void main() { }); test('round-trips varuint64 boundary values with Java-aligned lengths', () { - const cases = <({int bytes, int value})>[ - (bytes: 1, value: 0), - (bytes: 1, value: 1), - (bytes: 1, value: 1 << 6), - (bytes: 2, value: 1 << 7), - (bytes: 2, value: 1 << 13), - (bytes: 3, value: 1 << 14), - (bytes: 3, value: 1 << 20), - (bytes: 4, value: 1 << 21), - (bytes: 4, value: 1 << 27), - (bytes: 5, value: 1 << 28), - (bytes: 5, value: 1 << 34), - (bytes: 6, value: 1 << 35), - (bytes: 6, value: 1 << 41), - (bytes: 7, value: 1 << 42), - (bytes: 7, value: 1 << 48), - (bytes: 8, value: 1 << 49), - (bytes: 8, value: 1 << 55), - (bytes: 9, value: 1 << 56), - (bytes: 9, value: 1 << 62), - (bytes: 9, value: 0x7fffffffffffffff), - (bytes: 9, value: -0x8000000000000000), - (bytes: 9, value: 0xffffffffffffffff), + final cases = <({int bytes, Uint64 value})>[ + (bytes: 1, value: Uint64(0)), + (bytes: 1, value: Uint64(1)), + (bytes: 1, value: _u64Pow2(6)), + (bytes: 2, value: _u64Pow2(7)), + (bytes: 2, value: _u64Pow2(13)), + (bytes: 3, value: _u64Pow2(14)), + (bytes: 3, value: _u64Pow2(20)), + (bytes: 4, value: _u64Pow2(21)), + (bytes: 4, value: _u64Pow2(27)), + (bytes: 5, value: _u64Pow2(28)), + (bytes: 5, value: _u64Pow2(34)), + (bytes: 6, value: _u64Pow2(35)), + (bytes: 6, value: _u64Pow2(41)), + (bytes: 7, value: _u64Pow2(42)), + (bytes: 7, value: _u64Pow2(48)), + (bytes: 8, value: _u64Pow2(49)), + (bytes: 8, value: _u64Pow2(55)), + (bytes: 9, value: _u64Pow2(56)), + (bytes: 9, value: _u64Pow2(62)), + (bytes: 9, value: _u64Hex('7fffffffffffffff')), + (bytes: 9, value: _u64Hex('8000000000000000')), + (bytes: 9, value: _u64Hex('ffffffffffffffff')), ]; for (final testCase in cases) { - _expectEncodedIntRoundTrip( + _expectEncodedUint64RoundTrip( value: testCase.value, expectedBytes: testCase.bytes, write: (buffer, value) => buffer.writeVarUint64(value), @@ -227,33 +235,33 @@ void main() { }); test('round-trips varint64 boundary values with Java-aligned lengths', () { - const cases = <({int bytes, int value})>[ - (bytes: 1, value: 0), - (bytes: 1, value: 1), - (bytes: 1, value: -1), - (bytes: 1, value: -64), - (bytes: 2, value: 1 << 6), - (bytes: 2, value: -128), - (bytes: 3, value: 1 << 13), - (bytes: 3, value: -16384), - (bytes: 4, value: 1 << 20), - (bytes: 4, value: -2097152), - (bytes: 5, value: 1 << 27), - (bytes: 5, value: -268435456), - (bytes: 6, value: 1 << 34), - (bytes: 6, value: -34359738368), - (bytes: 7, value: 1 << 42), - (bytes: 7, value: -4398046511104), - (bytes: 8, value: 1 << 49), - (bytes: 8, value: -562949953421312), - (bytes: 9, value: 1 << 55), - (bytes: 9, value: -72057594037927936), - (bytes: 9, value: 0x7fffffffffffffff), - (bytes: 9, value: -0x8000000000000000), + final cases = <({int bytes, Int64 value})>[ + (bytes: 1, value: Int64(0)), + (bytes: 1, value: Int64(1)), + (bytes: 1, value: Int64(-1)), + (bytes: 1, value: Int64(-64)), + (bytes: 2, value: _i64Pow2(6)), + (bytes: 2, value: Int64(-128)), + (bytes: 3, value: _i64Pow2(13)), + (bytes: 3, value: Int64(-16384)), + (bytes: 4, value: _i64Pow2(20)), + (bytes: 4, value: Int64(-2097152)), + (bytes: 5, value: _i64Pow2(27)), + (bytes: 5, value: Int64(-268435456)), + (bytes: 6, value: _i64Pow2(34)), + (bytes: 6, value: -_i64Pow2(35)), + (bytes: 7, value: _i64Pow2(42)), + (bytes: 7, value: -_i64Pow2(42)), + (bytes: 8, value: _i64Pow2(49)), + (bytes: 8, value: -_i64Pow2(49)), + (bytes: 9, value: _i64Pow2(55)), + (bytes: 9, value: -_i64Pow2(56)), + (bytes: 9, value: _i64Hex('7fffffffffffffff')), + (bytes: 9, value: _i64Hex('8000000000000000')), ]; for (final testCase in cases) { - _expectEncodedIntRoundTrip( + _expectEncodedInt64RoundTrip( value: testCase.value, expectedBytes: testCase.bytes, write: (buffer, value) => buffer.writeVarInt64(value), @@ -265,21 +273,21 @@ void main() { test('round-trips tagged int64 boundary values with Java-aligned lengths', () { - const cases = <({int bytes, int value})>[ - (bytes: 4, value: -0x40000000), - (bytes: 4, value: -1), - (bytes: 4, value: 0), - (bytes: 4, value: 1), - (bytes: 4, value: 1 << 28), - (bytes: 4, value: 0x3fffffff), - (bytes: 9, value: -0x40000001), - (bytes: 9, value: 0x40000000), - (bytes: 9, value: 0x7fffffffffffffff), - (bytes: 9, value: -0x8000000000000000), + final cases = <({int bytes, Int64 value})>[ + (bytes: 4, value: Int64(-0x40000000)), + (bytes: 4, value: Int64(-1)), + (bytes: 4, value: Int64(0)), + (bytes: 4, value: Int64(1)), + (bytes: 4, value: Int64(1 << 28)), + (bytes: 4, value: Int64(0x3fffffff)), + (bytes: 9, value: Int64(-0x40000001)), + (bytes: 9, value: Int64(0x40000000)), + (bytes: 9, value: _i64Hex('7fffffffffffffff')), + (bytes: 9, value: _i64Hex('8000000000000000')), ]; for (final testCase in cases) { - _expectEncodedIntRoundTrip( + _expectEncodedInt64RoundTrip( value: testCase.value, expectedBytes: testCase.bytes, write: (buffer, value) => buffer.writeTaggedInt64(value), @@ -292,20 +300,20 @@ void main() { test( 'round-trips tagged uint64 boundary values with Java-aligned lengths', () { - const cases = <({int bytes, int value})>[ - (bytes: 4, value: 0), - (bytes: 4, value: 1), - (bytes: 4, value: 1 << 30), - (bytes: 4, value: 0x7fffffff), - (bytes: 9, value: 0x80000000), - (bytes: 9, value: 0x100000000), - (bytes: 9, value: 0x7fffffffffffffff), - (bytes: 9, value: -0x8000000000000000), - (bytes: 9, value: 0xffffffffffffffff), + final cases = <({int bytes, Uint64 value})>[ + (bytes: 4, value: Uint64(0)), + (bytes: 4, value: Uint64(1)), + (bytes: 4, value: Uint64(1 << 30)), + (bytes: 4, value: Uint64(0x7fffffff)), + (bytes: 9, value: Uint64(0x80000000)), + (bytes: 9, value: _u64Pow2(32)), + (bytes: 9, value: _u64Hex('7fffffffffffffff')), + (bytes: 9, value: _u64Hex('8000000000000000')), + (bytes: 9, value: _u64Hex('ffffffffffffffff')), ]; for (final testCase in cases) { - _expectEncodedIntRoundTrip( + _expectEncodedUint64RoundTrip( value: testCase.value, expectedBytes: testCase.bytes, write: (buffer, value) => buffer.writeTaggedUint64(value), @@ -375,18 +383,18 @@ void main() { buffer.writeUint16(65000); buffer.writeInt32(-123456789); buffer.writeUint32(0x89abcdef); - buffer.writeInt64(-0x1234567890abcdef); - buffer.writeUint64(0xfedcba9876543210); + buffer.writeInt64(_i64Hex('-1234567890abcdef')); + buffer.writeUint64(_u64Hex('fedcba9876543210')); buffer.writeFloat16(Float16(1.5)); buffer.writeBfloat16(Bfloat16(2.5)); buffer.writeFloat32(3.25); buffer.writeFloat64(-9.5); buffer.writeVarUint32(0xffffffff); buffer.writeVarInt32(-0x40000000); - buffer.writeVarUint64(0xffffffffffffffff); - buffer.writeVarInt64(-0x4000000000000000); - buffer.writeTaggedInt64(0x40000000); - buffer.writeTaggedUint64(0x80000000); + buffer.writeVarUint64(_u64Hex('ffffffffffffffff')); + buffer.writeVarInt64(_i64Hex('c000000000000000')); + buffer.writeTaggedInt64(Int64(0x40000000)); + buffer.writeTaggedUint64(Uint64(0x80000000)); final cursor = GeneratedWriteCursor.reserve(generated, 128); cursor.writeBool(true); @@ -396,18 +404,18 @@ void main() { cursor.writeUint16(65000); cursor.writeInt32(-123456789); cursor.writeUint32(0x89abcdef); - cursor.writeInt64(-0x1234567890abcdef); - cursor.writeUint64(0xfedcba9876543210); + cursor.writeInt64(_i64Hex('-1234567890abcdef')); + cursor.writeUint64(_u64Hex('fedcba9876543210')); cursor.writeFloat16(Float16(1.5)); cursor.writeBfloat16(Bfloat16(2.5)); cursor.writeFloat32(3.25); cursor.writeFloat64(-9.5); cursor.writeVarUint32(0xffffffff); cursor.writeVarInt32(-0x40000000); - cursor.writeVarUint64(0xffffffffffffffff); - cursor.writeVarInt64(-0x4000000000000000); - cursor.writeTaggedInt64(0x40000000); - cursor.writeTaggedUint64(0x80000000); + cursor.writeVarUint64(_u64Hex('ffffffffffffffff')); + cursor.writeVarInt64(_i64Hex('c000000000000000')); + cursor.writeTaggedInt64(Int64(0x40000000)); + cursor.writeTaggedUint64(Uint64(0x80000000)); cursor.finish(); expect(generated.toBytes(), orderedEquals(buffer.toBytes())); @@ -422,18 +430,18 @@ void main() { expect(readCursor.readUint16(), equals(65000)); expect(readCursor.readInt32(), equals(-123456789)); expect(readCursor.readUint32(), equals(0x89abcdef)); - expect(readCursor.readInt64(), equals(-0x1234567890abcdef)); - expect(readCursor.readUint64(), equals(0xfedcba9876543210)); + expect(readCursor.readInt64(), equals(_i64Hex('-1234567890abcdef'))); + expect(readCursor.readUint64(), equals(_u64Hex('fedcba9876543210'))); expect(readCursor.readFloat16(), equals(Float16(1.5))); expect(readCursor.readBfloat16(), equals(Bfloat16(2.5))); expect(readCursor.readFloat32(), closeTo(3.25, 0.0001)); expect(readCursor.readFloat64(), equals(-9.5)); expect(readCursor.readVarUint32(), equals(0xffffffff)); expect(readCursor.readVarInt32(), equals(-0x40000000)); - expect(readCursor.readVarUint64(), equals(0xffffffffffffffff)); - expect(readCursor.readVarInt64(), equals(-0x4000000000000000)); - expect(readCursor.readTaggedInt64(), equals(0x40000000)); - expect(readCursor.readTaggedUint64(), equals(0x80000000)); + expect(readCursor.readVarUint64(), equals(_u64Hex('ffffffffffffffff'))); + expect(readCursor.readVarInt64(), equals(_i64Hex('c000000000000000'))); + expect(readCursor.readTaggedInt64(), equals(Int64(0x40000000))); + expect(readCursor.readTaggedUint64(), equals(Uint64(0x80000000))); readCursor.finish(); expect(readBuffer.readableBytes, equals(0)); @@ -466,3 +474,55 @@ void _expectEncodedIntRoundTrip({ expect(cursorBuffer.readableBytes, equals(0)); } } + +void _expectEncodedUint64RoundTrip({ + required Uint64 value, + required int expectedBytes, + required void Function(Buffer buffer, Uint64 value) write, + required Uint64 Function(Buffer buffer) read, + Uint64 Function(GeneratedReadCursor cursor)? cursorRead, +}) { + final buffer = Buffer(); + write(buffer, value); + final bytes = Uint8List.fromList(buffer.toBytes()); + + expect(bytes.length, equals(expectedBytes)); + + final wrapped = Buffer.wrap(Uint8List.fromList(bytes)); + expect(read(wrapped), equals(value)); + expect(wrapped.readableBytes, equals(0)); + + if (cursorRead != null) { + final cursorBuffer = Buffer.wrap(Uint8List.fromList(bytes)); + final cursor = GeneratedReadCursor.start(cursorBuffer); + expect(cursorRead(cursor), equals(value)); + cursor.finish(); + expect(cursorBuffer.readableBytes, equals(0)); + } +} + +void _expectEncodedInt64RoundTrip({ + required Int64 value, + required int expectedBytes, + required void Function(Buffer buffer, Int64 value) write, + required Int64 Function(Buffer buffer) read, + Int64 Function(GeneratedReadCursor cursor)? cursorRead, +}) { + final buffer = Buffer(); + write(buffer, value); + final bytes = Uint8List.fromList(buffer.toBytes()); + + expect(bytes.length, equals(expectedBytes)); + + final wrapped = Buffer.wrap(Uint8List.fromList(bytes)); + expect(read(wrapped), equals(value)); + expect(wrapped.readableBytes, equals(0)); + + if (cursorRead != null) { + final cursorBuffer = Buffer.wrap(Uint8List.fromList(bytes)); + final cursor = GeneratedReadCursor.start(cursorBuffer); + expect(cursorRead(cursor), equals(value)); + cursor.finish(); + expect(cursorBuffer.readableBytes, equals(0)); + } +} diff --git a/dart/packages/fory/test/codegen_conversion_expression_test.dart b/dart/packages/fory/test/codegen_conversion_expression_test.dart index 1da2039cd4..7982787cc7 100644 --- a/dart/packages/fory/test/codegen_conversion_expression_test.dart +++ b/dart/packages/fory/test/codegen_conversion_expression_test.dart @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +@TestOn('vm') +library; + import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:fory/fory.dart'; diff --git a/dart/packages/fory/test/enum_union_serializer_test.dart b/dart/packages/fory/test/enum_union_serializer_test.dart index 1b65d85f09..2d1a822201 100644 --- a/dart/packages/fory/test/enum_union_serializer_test.dart +++ b/dart/packages/fory/test/enum_union_serializer_test.dart @@ -95,7 +95,7 @@ final class TestUnion { factory TestUnion.ofString(String value) => TestUnion._(0, value); - factory TestUnion.ofInt(int value) => TestUnion._(1, value); + factory TestUnion.ofInt(Int64 value) => TestUnion._(1, value); factory TestUnion.ofLeaf(UnionLeaf value) => TestUnion._(2, value); @@ -122,9 +122,12 @@ final class TestUnionSerializer extends UnionSerializer { if (index == 0 && value is String) { return TestUnion.ofString(value); } - if (index == 1 && value is int) { + if (index == 1 && value is Int64) { return TestUnion.ofInt(value); } + if (index == 1 && value is int) { + return TestUnion.ofInt(Int64(value)); + } if (index == 2 && value is UnionLeaf) { return TestUnion.ofLeaf(value); } @@ -230,7 +233,7 @@ void main() { final cases = [ TestUnion.ofString('alpha'), - TestUnion.ofInt(1234), + TestUnion.ofInt(Int64(1234)), TestUnion.ofLeaf(UnionLeaf()..label = 'leaf'), ]; diff --git a/dart/packages/fory/test/manual_registration_test.dart b/dart/packages/fory/test/manual_registration_test.dart index e6604b2bf1..1558478e37 100644 --- a/dart/packages/fory/test/manual_registration_test.dart +++ b/dart/packages/fory/test/manual_registration_test.dart @@ -24,7 +24,7 @@ final class ManualValue { ManualValue(this.name, this.score); final String name; - final int score; + final Int64 score; } final class ManualValueSerializer extends Serializer { @@ -121,11 +121,11 @@ void main() { typeName: 'ManualValue', ); - final value = ManualValue('alpha', 99); + final value = ManualValue('alpha', Int64(99)); final bytes = fory.serialize(value); final roundTrip = fory.deserialize(bytes); expect(roundTrip.name, equals('alpha')); - expect(roundTrip.score, equals(99)); + expect(roundTrip.score, equals(Int64(99))); }); test('writeNonRef does not seed later back-references', () { diff --git a/dart/packages/fory/test/numeric_wrapper_test.dart b/dart/packages/fory/test/numeric_wrapper_test.dart index a944761f87..0945940e2d 100644 --- a/dart/packages/fory/test/numeric_wrapper_test.dart +++ b/dart/packages/fory/test/numeric_wrapper_test.dart @@ -24,6 +24,15 @@ import 'package:test/test.dart'; part 'numeric_wrapper_test.fory.dart'; +Uint64 _u64Hex(String value) => Uint64.parseHex(value); + +double _float64FromWords(int low32, int high32) { + final bytes = ByteData(8) + ..setUint32(0, low32, Endian.little) + ..setUint32(4, high32, Endian.little); + return bytes.getFloat64(0, Endian.little); +} + @ForyStruct() class NumericWrappersEnvelope { NumericWrappersEnvelope(); @@ -65,12 +74,12 @@ NumericWrappersEnvelope _sampleEnvelope() { ..u8 = Uint8(0xff) ..u16 = Uint16(0xffff) ..u32 = Uint32(0xffffffff) - ..u64 = Uint64(0xffffffffffffffff) + ..u64 = _u64Hex('ffffffffffffffff') ..half = Float16.fromBits(0x3555) ..brain = Bfloat16.fromBits(0x3eab) ..single = Float32.fromBits(0x40490fdb) ..optionalI8 = Int8(126) - ..optionalU64 = Uint64(0x8000000000000000) + ..optionalU64 = _u64Hex('8000000000000000') ..optionalHalf = Float16.fromBits(0x8000) ..optionalBrain = Bfloat16.fromBits(0x8000) ..optionalSingle = Float32.fromBits(0x80000000); @@ -143,10 +152,12 @@ void main() { expect( Uint32(0xf0f0f0f0) & Uint32(0x0ff00ff0), equals(Uint32(0x00f000f0))); - expect(Uint64(0xffffffffffffffff) + 1, equals(Uint64(0))); - expect(Uint64(0) - 1, equals(Uint64(0xffffffffffffffff))); + expect(_u64Hex('ffffffffffffffff') + 1, equals(Uint64(0))); + expect(Uint64(0) - 1, equals(_u64Hex('ffffffffffffffff'))); expect( - Uint64(0x123456789abcdef0) >> 4, equals(Uint64(0x0123456789abcdef))); + _u64Hex('123456789abcdef0') >> 4, + equals(_u64Hex('0123456789abcdef')), + ); expect(Uint64(0xff).toInt(), equals(0xff)); }); @@ -181,12 +192,8 @@ void main() { test( 'Bfloat16.fromDouble rounds directly from float64 and preserves NaN sign payload bits', () { - final subnormalSource = ByteData(8) - ..setUint64(0, 0x37da834f7e281cc1, Endian.little); - final trickySubnormal = subnormalSource.getFloat64(0, Endian.little); - final nanSource = ByteData(8) - ..setUint64(0, 0xfff123456789abcd, Endian.little); - final payloadNaN = nanSource.getFloat64(0, Endian.little); + final trickySubnormal = _float64FromWords(0x7e281cc1, 0x37da834f); + final payloadNaN = _float64FromWords(0x6789abcd, 0xfff12345); final convertedNaN = Bfloat16.fromDouble(payloadNaN); expect(Bfloat16.fromDouble(trickySubnormal).toBits(), equals(0x0007)); @@ -223,7 +230,7 @@ void main() { expect(_roundTrip(fory, Uint32(-1)), equals(Uint32(0xffffffff))); expect( _roundTrip(fory, Uint64(-1)), - equals(Uint64(0xffffffffffffffff)), + equals(_u64Hex('ffffffffffffffff')), ); }); diff --git a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart index 2a03eb0806..97b8b85128 100644 --- a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart +++ b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart @@ -24,6 +24,9 @@ import 'package:test/test.dart'; part 'scalar_and_typed_array_serializer_test.fory.dart'; +Timestamp _timestamp(int seconds, int nanoseconds) => + Timestamp(Int64(seconds), nanoseconds); + @ForyStruct() class ScalarAndArrayEnvelope { ScalarAndArrayEnvelope(); @@ -45,7 +48,7 @@ class ScalarAndArrayEnvelope { Bfloat16 brain = Bfloat16(0); Float32 single = Float32(0); LocalDate date = const LocalDate(1970, 1, 1); - Timestamp timestamp = const Timestamp(0, 0); + Timestamp timestamp = _timestamp(0, 0); } void _registerScalarTypes(Fory fory) { @@ -88,7 +91,7 @@ ScalarAndArrayEnvelope _sampleEnvelope() { ..brain = const Bfloat16.fromBits(0x7fc0) ..single = Float32(3.5) ..date = LocalDate.fromEpochDay(-1) - ..timestamp = const Timestamp(-123, 456789123); + ..timestamp = _timestamp(-123, 456789123); } void _expectUint8ListEquals(Uint8List actual, Uint8List expected) { @@ -205,7 +208,7 @@ void main() { ); final negativeTimestamp = _roundTripRoot( fory, - const Timestamp(-123, 456789000), + _timestamp(-123, 456789000), ); final fromDateTime = _roundTripRoot( fory, @@ -216,7 +219,7 @@ void main() { expect(leapDay, equals(const LocalDate(2024, 2, 29))); expect( negativeTimestamp, - equals(const Timestamp(-123, 456789000).toDateTime()), + equals(_timestamp(-123, 456789000).toDateTime()), ); expect( fromDateTime, diff --git a/dart/packages/fory/test/time_serializer_test.dart b/dart/packages/fory/test/time_serializer_test.dart index 9ca5745e39..275362bd3e 100644 --- a/dart/packages/fory/test/time_serializer_test.dart +++ b/dart/packages/fory/test/time_serializer_test.dart @@ -20,17 +20,19 @@ import 'dart:typed_data'; import 'package:fory/fory.dart'; -import 'package:fory/src/meta/type_ids.dart'; import 'package:test/test.dart'; part 'time_serializer_test.fory.dart'; +Timestamp _timestamp(int seconds, int nanoseconds) => + Timestamp(Int64(seconds), nanoseconds); + @ForyStruct() class TimeEnvelope { TimeEnvelope(); LocalDate date = const LocalDate(1970, 1, 1); - Timestamp timestamp = const Timestamp(0, 0); + Timestamp timestamp = _timestamp(0, 0); DateTime instant = DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true); Duration duration = Duration.zero; LocalDate? optionalDate; @@ -62,7 +64,7 @@ void _expectTimeEnvelope(TimeEnvelope actual, TimeEnvelope expected) { TimeEnvelope _sampleTimeEnvelope() { return TimeEnvelope() ..date = const LocalDate(2024, 2, 29) - ..timestamp = const Timestamp(-123456789, 987654321) + ..timestamp = _timestamp(-123456789, 987654321) ..instant = DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true) ..duration = const Duration(days: 2, seconds: 3, microseconds: 456789) ..optionalDate = LocalDate.fromEpochDay(-1) @@ -113,15 +115,15 @@ void main() { final fory = Fory(); final cases = >[ MapEntry( - const Timestamp(0, 0), + _timestamp(0, 0), DateTime.fromMicrosecondsSinceEpoch(0, isUtc: true), ), MapEntry( - const Timestamp(-1, 1000), + _timestamp(-1, 1000), DateTime.fromMicrosecondsSinceEpoch(-999999, isUtc: true), ), MapEntry( - const Timestamp(1, 999999000), + _timestamp(1, 999999000), DateTime.fromMicrosecondsSinceEpoch(1999999, isUtc: true), ), MapEntry( @@ -157,7 +159,7 @@ void main() { () { final fory = Fory(); final roundTrip = fory.deserialize>( - fory.serialize([const Timestamp(-1, 999999000)]), + fory.serialize([_timestamp(-1, 999999000)]), ); expect( @@ -175,7 +177,7 @@ void main() { expect( fory.deserialize( - fory.serialize(const Timestamp(-1, 999999000))), + fory.serialize(_timestamp(-1, 999999000))), equals(DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true)), ); }); @@ -195,11 +197,11 @@ void main() { final cases = >[ MapEntry( DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true), - const Timestamp(-1, 999999000), + _timestamp(-1, 999999000), ), MapEntry( DateTime.fromMicrosecondsSinceEpoch(-1000001, isUtc: true), - const Timestamp(-2, 999999000), + _timestamp(-2, 999999000), ), ]; @@ -249,7 +251,7 @@ void main() { test('does not preserve references for repeated temporal values', () { final fory = Fory(); final duration = Duration(microseconds: 1); - final timestamp = Timestamp(-1, 1000); + final timestamp = _timestamp(-1, 1000); final date = LocalDate.fromEpochDay(-1); final roundTrip = fory.deserialize>( @@ -354,7 +356,7 @@ void main() { final fory = Fory(); expect( - () => fory.serialize(const Timestamp(0, 1000000000)), + () => fory.serialize(_timestamp(0, 1000000000)), throwsA( isA().having( (error) => error.toString(), @@ -367,7 +369,7 @@ void main() { test('rejects timestamp payloads with nanoseconds outside spec range', () { final fory = Fory(); - final bytes = Uint8List.fromList(fory.serialize(const Timestamp(0, 0))); + final bytes = Uint8List.fromList(fory.serialize(_timestamp(0, 0))); final view = ByteData.sublistView(bytes); view.setUint32(bytes.length - 4, 1000000000, Endian.little); diff --git a/dart/packages/fory/test/unsigned_serializer_test.dart b/dart/packages/fory/test/unsigned_serializer_test.dart index 7b39f7b7f5..1f7548e674 100644 --- a/dart/packages/fory/test/unsigned_serializer_test.dart +++ b/dart/packages/fory/test/unsigned_serializer_test.dart @@ -24,8 +24,8 @@ import 'package:test/test.dart'; part 'unsigned_serializer_test.fory.dart'; const int _uint32Midpoint = 0x80000000; -const int _uint64Midpoint = 0x8000000000000000; -const int _uint64Max = 0xffffffffffffffff; +final Uint64 _uint64Midpoint = Uint64.parseHex('8000000000000000'); +final Uint64 _uint64Max = Uint64.parseHex('ffffffffffffffff'); @ForyStruct() class UnsignedFields { @@ -44,13 +44,13 @@ class UnsignedFields { int u32Fixed = 0; @Uint64Type(encoding: LongEncoding.varint) - int u64Var = 0; + Uint64 u64Var = Uint64(0); @Uint64Type(encoding: LongEncoding.fixed) - int u64Fixed = 0; + Uint64 u64Fixed = Uint64(0); @Uint64Type(encoding: LongEncoding.tagged) - int u64Tagged = 0; + Uint64 u64Tagged = Uint64(0); @Uint8Type() int? u8Nullable; @@ -65,13 +65,13 @@ class UnsignedFields { int? u32FixedNullable; @Uint64Type(encoding: LongEncoding.varint) - int? u64VarNullable; + Uint64? u64VarNullable; @Uint64Type(encoding: LongEncoding.fixed) - int? u64FixedNullable; + Uint64? u64FixedNullable; @Uint64Type(encoding: LongEncoding.tagged) - int? u64TaggedNullable; + Uint64? u64TaggedNullable; } @ForyStruct() @@ -144,9 +144,9 @@ UnsignedFields _nullUnsignedFields() { ..u16 = 2 ..u32Var = 3 ..u32Fixed = 4 - ..u64Var = 5 - ..u64Fixed = 6 - ..u64Tagged = 7 + ..u64Var = Uint64(5) + ..u64Fixed = Uint64(6) + ..u64Tagged = Uint64(7) ..u8Nullable = null ..u16Nullable = null ..u32VarNullable = null From 5fb23171dea48288538e386cda1d06bc8fce276c Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 09:12:23 +0800 Subject: [PATCH 02/14] ci(dart): run web tests --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa0acf2118..243cffa288 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -999,6 +999,10 @@ jobs: run: | cd dart/packages/fory-test dart test + - name: Run web tests + run: | + cd dart/packages/fory + dart test -p chrome - name: Run code analysis run: | cd dart/packages/fory-test From 11a29bbda5625e48fcbc3fdae549cb038ae10926 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 09:22:48 +0800 Subject: [PATCH 03/14] fix(dart): update xlang int64 analysis --- .../fory-test/test/cross_lang_test/xlang_test_main.dart | 4 ++-- dart/packages/fory/lib/src/codegen/fory_generator.dart | 2 +- dart/packages/fory/lib/src/resolver/type_resolver.dart | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dart/packages/fory-test/test/cross_lang_test/xlang_test_main.dart b/dart/packages/fory-test/test/cross_lang_test/xlang_test_main.dart index 0f66eadbd6..2509f84660 100644 --- a/dart/packages/fory-test/test/cross_lang_test/xlang_test_main.dart +++ b/dart/packages/fory-test/test/cross_lang_test/xlang_test_main.dart @@ -244,8 +244,8 @@ void _verifyDecimalCase() { Uint8List _hashBytes(int low, int high) { final buffer = Buffer(); - buffer.writeInt64(low); - buffer.writeInt64(high); + buffer.writeInt64(Int64(low)); + buffer.writeInt64(Int64(high)); return buffer.toBytes(); } diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index 7bee0cd5f0..0ee6f31b13 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -95,7 +95,7 @@ final class ForyGenerator extends Generator { annotatedClasses.map(_analyzeStruct).toList(growable: false); final output = StringBuffer() ..writeln( - '// ignore_for_file: implementation_imports, invalid_use_of_internal_member, no_leading_underscores_for_local_identifiers, unused_element, unused_element_parameter, unnecessary_null_comparison', + '// ignore_for_file: implementation_imports, invalid_use_of_internal_member, no_leading_underscores_for_local_identifiers, unreachable_switch_case, unused_element, unused_element_parameter, unnecessary_null_comparison', ) ..writeln(); diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index a62269d1c7..dcc001832d 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -324,6 +324,9 @@ final class TypeResolver { if (value is Int32) { return _builtin(Int32, TypeIds.varInt32); } + // The native extension type is represented as `int`, but web needs this + // branch to keep explicit Int64 wrapper values distinct from plain ints. + // ignore: unnecessary_type_check if (value is Int64 && value is! int) { return _builtin(Int64, TypeIds.int64); } From 403d5a5a44cdbb7a1b8c5021d3d7f2dfaca70ca9 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 10:54:40 +0800 Subject: [PATCH 04/14] fix(dart): expose epoch day as int64 --- .../fory/lib/src/codegen/fory_generator.dart | 4 ++-- .../lib/src/serializer/time_serializers.dart | 12 +++++------ .../fory/lib/src/types/local_date.dart | 13 +++++++----- ...calar_and_typed_array_serializer_test.dart | 6 +++--- .../fory/test/time_serializer_test.dart | 20 +++++++++---------- docs/guide/dart/supported-types.md | 2 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index 0ee6f31b13..dcd92d2791 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -1344,7 +1344,7 @@ GeneratedFieldType( case TypeIds.float64: return '$cursorExpression.writeFloat64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.date: - return '$cursorExpression.writeVarInt64(Int64($valueExpression.toEpochDay()))'; + return '$cursorExpression.writeVarInt64($valueExpression.toEpochDay())'; case TypeIds.duration: return '$cursorExpression.writeVarInt64(generatedDurationWireSeconds($valueExpression)); $cursorExpression.writeInt32(generatedDurationWireNanoseconds($valueExpression))'; case TypeIds.timestamp: @@ -1549,7 +1549,7 @@ GeneratedFieldType( case TypeIds.float64: return '$cursorExpression.readFloat64()'; case TypeIds.date: - return 'LocalDate.fromEpochDay($cursorExpression.readVarInt64().toInt())'; + return 'LocalDate.fromEpochDay($cursorExpression.readVarInt64())'; case TypeIds.duration: return 'readGeneratedDurationFromWire($cursorExpression.readVarInt64(), $cursorExpression.readInt32())'; case TypeIds.timestamp: diff --git a/dart/packages/fory/lib/src/serializer/time_serializers.dart b/dart/packages/fory/lib/src/serializer/time_serializers.dart index 74de18416a..6b607a455a 100644 --- a/dart/packages/fory/lib/src/serializer/time_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/time_serializers.dart @@ -86,7 +86,7 @@ Duration durationFromWire(Int64 seconds, int nanoseconds) { } final totalNanoseconds = seconds.toBigInt() * BigInt.from(_nanosecondsPerSecond) + - BigInt.from(nanoseconds); + BigInt.from(nanoseconds); final microseconds = (totalNanoseconds ~/ BigInt.from(1000)).toInt(); final value = Duration(microseconds: microseconds); if (nanoseconds.remainder(1000) != 0) { @@ -133,9 +133,9 @@ Timestamp timestampFromWire(Int64 seconds, int nanoseconds) { DateTime dateTimeFromWire(Int64 seconds, int nanoseconds) { _validateDateTimeNanoseconds(nanoseconds); - final microseconds = seconds.toBigInt() * - BigInt.from(Duration.microsecondsPerSecond) + - BigInt.from(nanoseconds ~/ 1000); + final microseconds = + seconds.toBigInt() * BigInt.from(Duration.microsecondsPerSecond) + + BigInt.from(nanoseconds ~/ 1000); return DateTime.fromMicrosecondsSinceEpoch( microseconds.toInt(), isUtc: true, @@ -150,12 +150,12 @@ final class LocalDateSerializer extends Serializer { @override void write(WriteContext context, LocalDate value) { - context.buffer.writeVarInt64(Int64(value.toEpochDay())); + context.buffer.writeVarInt64(value.toEpochDay()); } @override LocalDate read(ReadContext context) { - return LocalDate.fromEpochDay(context.buffer.readVarInt64().toInt()); + return LocalDate.fromEpochDay(context.buffer.readVarInt64()); } } diff --git a/dart/packages/fory/lib/src/types/local_date.dart b/dart/packages/fory/lib/src/types/local_date.dart index acd72fc3c4..6a1c31a322 100644 --- a/dart/packages/fory/lib/src/types/local_date.dart +++ b/dart/packages/fory/lib/src/types/local_date.dart @@ -17,6 +17,8 @@ * under the License. */ +import 'package:fory/src/types/int64.dart'; + /// Calendar date without time-of-day or time-zone information. final class LocalDate implements Comparable { /// Year component. @@ -32,9 +34,9 @@ final class LocalDate implements Comparable { const LocalDate(this.year, this.month, this.day); /// Creates a date from the xlang epoch-day representation. - factory LocalDate.fromEpochDay(int epochDay) { + factory LocalDate.fromEpochDay(Int64 epochDay) { final instant = DateTime.fromMillisecondsSinceEpoch( - epochDay * Duration.millisecondsPerDay, + epochDay.toInt() * Duration.millisecondsPerDay, isUtc: true, ); return LocalDate(instant.year, instant.month, instant.day); @@ -47,9 +49,10 @@ final class LocalDate implements Comparable { } /// Converts this date to xlang epoch-day form. - int toEpochDay() => - DateTime.utc(year, month, day).millisecondsSinceEpoch ~/ - Duration.millisecondsPerDay; + Int64 toEpochDay() => Int64( + DateTime.utc(year, month, day).millisecondsSinceEpoch ~/ + Duration.millisecondsPerDay, + ); /// Converts this date to a UTC [DateTime] at midnight. DateTime toDateTime() => DateTime.utc(year, month, day); diff --git a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart index 97b8b85128..b67b6dbc17 100644 --- a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart +++ b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart @@ -90,7 +90,7 @@ ScalarAndArrayEnvelope _sampleEnvelope() { ..half = const Float16.fromBits(0x8000) ..brain = const Bfloat16.fromBits(0x7fc0) ..single = Float32(3.5) - ..date = LocalDate.fromEpochDay(-1) + ..date = LocalDate.fromEpochDay(Int64(-1)) ..timestamp = _timestamp(-123, 456789123); } @@ -200,7 +200,7 @@ void main() { final beforeEpoch = _roundTripRoot( fory, - LocalDate.fromEpochDay(-1), + LocalDate.fromEpochDay(Int64(-1)), ); final leapDay = _roundTripRoot( fory, @@ -215,7 +215,7 @@ void main() { Timestamp.fromDateTime(DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700)), ); - expect(beforeEpoch, equals(LocalDate.fromEpochDay(-1))); + expect(beforeEpoch, equals(LocalDate.fromEpochDay(Int64(-1)))); expect(leapDay, equals(const LocalDate(2024, 2, 29))); expect( negativeTimestamp, diff --git a/dart/packages/fory/test/time_serializer_test.dart b/dart/packages/fory/test/time_serializer_test.dart index 275362bd3e..8715f845df 100644 --- a/dart/packages/fory/test/time_serializer_test.dart +++ b/dart/packages/fory/test/time_serializer_test.dart @@ -67,7 +67,7 @@ TimeEnvelope _sampleTimeEnvelope() { ..timestamp = _timestamp(-123456789, 987654321) ..instant = DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true) ..duration = const Duration(days: 2, seconds: 3, microseconds: 456789) - ..optionalDate = LocalDate.fromEpochDay(-1) + ..optionalDate = LocalDate.fromEpochDay(Int64(-1)) ..optionalTimestamp = Timestamp.fromDateTime( DateTime.utc(2024, 1, 2, 3, 4, 5, 6, 700), ) @@ -80,8 +80,8 @@ void main() { test('round-trips LocalDate edge cases', () { final fory = Fory(); final cases = [ - LocalDate.fromEpochDay(-1), - LocalDate.fromEpochDay(0), + LocalDate.fromEpochDay(Int64(-1)), + LocalDate.fromEpochDay(Int64(0)), const LocalDate(2024, 2, 29), const LocalDate(9999, 12, 31), ]; @@ -94,10 +94,11 @@ void main() { test('encodes LocalDate as signed varint64 in xlang payloads', () { final fory = Fory(); - final value = LocalDate.fromEpochDay(-1); + final value = LocalDate.fromEpochDay(Int64(-1)); final bytes = fory.serialize(value); - expect(bytes, equals(Uint8List.fromList([0x02, 0xff, TypeIds.date, 0x01]))); + expect( + bytes, equals(Uint8List.fromList([0x02, 0xff, TypeIds.date, 0x01]))); expect(fory.deserialize(bytes), equals(value)); }); @@ -106,8 +107,8 @@ void main() { final value = LocalDate.fromDateTime(DateTime.utc(2024, 1, 2, 3, 4, 5)); expect(value, equals(const LocalDate(2024, 1, 2))); - expect( - value.toEpochDay(), equals(const LocalDate(2024, 1, 2).toEpochDay())); + final Int64 epochDay = value.toEpochDay(); + expect(epochDay, equals(const LocalDate(2024, 1, 2).toEpochDay())); expect(value.toDateTime(), equals(DateTime.utc(2024, 1, 2))); }); @@ -176,8 +177,7 @@ void main() { final fory = Fory(); expect( - fory.deserialize( - fory.serialize(_timestamp(-1, 999999000))), + fory.deserialize(fory.serialize(_timestamp(-1, 999999000))), equals(DateTime.fromMicrosecondsSinceEpoch(-1, isUtc: true)), ); }); @@ -252,7 +252,7 @@ void main() { final fory = Fory(); final duration = Duration(microseconds: 1); final timestamp = _timestamp(-1, 1000); - final date = LocalDate.fromEpochDay(-1); + final date = LocalDate.fromEpochDay(Int64(-1)); final roundTrip = fory.deserialize>( fory.serialize( diff --git a/docs/guide/dart/supported-types.md b/docs/guide/dart/supported-types.md index 3d309ee5b5..8606ac5fed 100644 --- a/docs/guide/dart/supported-types.md +++ b/docs/guide/dart/supported-types.md @@ -82,7 +82,7 @@ final timeout = const Duration(seconds: 30); The temporal wrappers expose conversion helpers: - `Timestamp.fromDateTime(...)` and `timestamp.toDateTime()` -- `LocalDate.fromEpochDay(...)`, `date.toEpochDay()` +- `LocalDate.fromEpochDay(Int64(...))`, `date.toEpochDay()` returns `Int64` - `LocalDate.fromDateTime(...)` and `date.toDateTime()` `Duration` support in Dart is exact to microseconds. Incoming xlang duration From ee0167fc14e7810e1de32f8521ba6f939bea5cbd Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 11:43:51 +0800 Subject: [PATCH 05/14] fix(dart): cache compatible type metadata safely --- .../fory/lib/src/resolver/type_resolver.dart | 7 +- .../packages/fory/lib/src/util/hash_util.dart | 205 +++++++++--------- 2 files changed, 102 insertions(+), 110 deletions(-) diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index dcc001832d..5d2b215df3 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -865,17 +865,14 @@ final class TypeResolver { return wireTypeMetaForResolved(sharedTypes[index]); } final header = TypeHeader(buffer.readInt64()); - final cached = - config.compatible ? null : _parsedTypeMetaCache.lookup(header); + final cached = _parsedTypeMetaCache.lookup(header); if (cached != null) { header.skipRemaining(buffer); sharedTypes.add(cached); return wireTypeMetaForResolved(cached); } final resolved = _readTypeDefWithHeader(buffer, header); - if (!config.compatible) { - _parsedTypeMetaCache.remember(header, resolved); - } + _parsedTypeMetaCache.remember(header, resolved); sharedTypes.add(resolved); return wireTypeMetaForResolved(resolved); } diff --git a/dart/packages/fory/lib/src/util/hash_util.dart b/dart/packages/fory/lib/src/util/hash_util.dart index cc59013b2c..c49cdd0181 100644 --- a/dart/packages/fory/lib/src/util/hash_util.dart +++ b/dart/packages/fory/lib/src/util/hash_util.dart @@ -23,127 +23,133 @@ import 'package:fory/src/meta/field_info.dart'; import 'package:fory/src/meta/type_def.dart'; import 'package:fory/src/meta/type_ids.dart'; import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; const int _typeDefCompressMetaFlag = 1 << 9; const int _typeDefHasFieldsMetaFlag = 1 << 8; const int _typeDefMetaSizeMask = 0xff; const int _typeDefHashShift = 14; -final BigInt _mask64Big = (BigInt.one << 64) - BigInt.one; -final BigInt _signBit64Big = BigInt.one << 63; -final BigInt _metaStringHashMaskBig = - BigInt.parse('ffffffffffffff00', radix: 16); -final BigInt _c1Big = BigInt.parse('87c37b91114253d5', radix: 16); -final BigInt _c2Big = BigInt.parse('4cf5ad432745937f', radix: 16); +final Uint64 _metaStringHashMask = Uint64.fromWords(0xffffff00, 0xffffffff); +final Uint64 _c1 = Uint64.fromWords(0x114253d5, 0x87c37b91); +final Uint64 _c2 = Uint64.fromWords(0x2745937f, 0x4cf5ad43); +final Uint64 _fmixC1 = Uint64.fromWords(0xed558ccd, 0xff51afd7); +final Uint64 _fmixC2 = Uint64.fromWords(0x1a85ec53, 0xc4ceb9fe); -(int, int) murmurHash3X64_128(List bytes, {int seed = 47}) { - var h1 = seed & 0x00000000ffffffff; - var h2 = seed & 0x00000000ffffffff; +(Int64, Int64) murmurHash3X64_128(List bytes, {int seed = 47}) { + final hash = _murmurHash3X64_128Bits(bytes, seed: seed); + return (_int64FromUint64(hash.$1), _int64FromUint64(hash.$2)); +} + +(Uint64, Uint64) _murmurHash3X64_128Bits(List bytes, {int seed = 47}) { + var h1 = Uint64(seed & 0x00000000ffffffff); + var h2 = Uint64(seed & 0x00000000ffffffff); final blockCount = bytes.length ~/ 16; for (var index = 0; index < blockCount; index += 1) { var k1 = _readLongLittleEndian(bytes, index * 16); var k2 = _readLongLittleEndian(bytes, index * 16 + 8); - k1 = _mul64(k1, _c1Big); + k1 = k1 * _c1; k1 = _rotateLeft64(k1, 31); - k1 = _mul64(k1, _c2Big); - h1 = _toSigned64(h1 ^ k1); + k1 = k1 * _c2; + h1 = h1 ^ k1; h1 = _rotateLeft64(h1, 27); - h1 = _add64(h1, h2); - h1 = _add64(_mul64(h1, BigInt.from(5)), 0x52dce729); + h1 = h1 + h2; + h1 = (h1 * 5) + 0x52dce729; - k2 = _mul64(k2, _c2Big); + k2 = k2 * _c2; k2 = _rotateLeft64(k2, 33); - k2 = _mul64(k2, _c1Big); - h2 = _toSigned64(h2 ^ k2); + k2 = k2 * _c1; + h2 = h2 ^ k2; h2 = _rotateLeft64(h2, 31); - h2 = _add64(h2, h1); - h2 = _add64(_mul64(h2, BigInt.from(5)), 0x38495ab5); + h2 = h2 + h1; + h2 = (h2 * 5) + 0x38495ab5; } - var k1 = 0; - var k2 = 0; + var k1 = Uint64(0); + var k2 = Uint64(0); final tailOffset = blockCount * 16; final tailLength = bytes.length & 15; if (tailLength >= 15) { - k2 ^= (bytes[tailOffset + 14] & 0xff) << 48; + k2 = k2 ^ (Uint64(bytes[tailOffset + 14] & 0xff) << 48); } if (tailLength >= 14) { - k2 ^= (bytes[tailOffset + 13] & 0xff) << 40; + k2 = k2 ^ (Uint64(bytes[tailOffset + 13] & 0xff) << 40); } if (tailLength >= 13) { - k2 ^= (bytes[tailOffset + 12] & 0xff) << 32; + k2 = k2 ^ (Uint64(bytes[tailOffset + 12] & 0xff) << 32); } if (tailLength >= 12) { - k2 ^= (bytes[tailOffset + 11] & 0xff) << 24; + k2 = k2 ^ (Uint64(bytes[tailOffset + 11] & 0xff) << 24); } if (tailLength >= 11) { - k2 ^= (bytes[tailOffset + 10] & 0xff) << 16; + k2 = k2 ^ (Uint64(bytes[tailOffset + 10] & 0xff) << 16); } if (tailLength >= 10) { - k2 ^= (bytes[tailOffset + 9] & 0xff) << 8; + k2 = k2 ^ (Uint64(bytes[tailOffset + 9] & 0xff) << 8); } if (tailLength >= 9) { - k2 ^= bytes[tailOffset + 8] & 0xff; - k2 = _mul64(k2, _c2Big); + k2 = k2 ^ (bytes[tailOffset + 8] & 0xff); + k2 = k2 * _c2; k2 = _rotateLeft64(k2, 33); - k2 = _mul64(k2, _c1Big); - h2 = _toSigned64(h2 ^ k2); + k2 = k2 * _c1; + h2 = h2 ^ k2; } if (tailLength >= 8) { - k1 ^= _signedByte(bytes[tailOffset + 7]) << 56; + k1 = k1 ^ (Uint64(bytes[tailOffset + 7] & 0xff) << 56); } if (tailLength >= 7) { - k1 ^= (bytes[tailOffset + 6] & 0xff) << 48; + k1 = k1 ^ (Uint64(bytes[tailOffset + 6] & 0xff) << 48); } if (tailLength >= 6) { - k1 ^= (bytes[tailOffset + 5] & 0xff) << 40; + k1 = k1 ^ (Uint64(bytes[tailOffset + 5] & 0xff) << 40); } if (tailLength >= 5) { - k1 ^= (bytes[tailOffset + 4] & 0xff) << 32; + k1 = k1 ^ (Uint64(bytes[tailOffset + 4] & 0xff) << 32); } if (tailLength >= 4) { - k1 ^= (bytes[tailOffset + 3] & 0xff) << 24; + k1 = k1 ^ (Uint64(bytes[tailOffset + 3] & 0xff) << 24); } if (tailLength >= 3) { - k1 ^= (bytes[tailOffset + 2] & 0xff) << 16; + k1 = k1 ^ (Uint64(bytes[tailOffset + 2] & 0xff) << 16); } if (tailLength >= 2) { - k1 ^= (bytes[tailOffset + 1] & 0xff) << 8; + k1 = k1 ^ (Uint64(bytes[tailOffset + 1] & 0xff) << 8); } if (tailLength >= 1) { - k1 ^= bytes[tailOffset] & 0xff; - k1 = _mul64(k1, _c1Big); + k1 = k1 ^ (bytes[tailOffset] & 0xff); + k1 = k1 * _c1; k1 = _rotateLeft64(k1, 31); - k1 = _mul64(k1, _c2Big); - h1 = _toSigned64(h1 ^ k1); + k1 = k1 * _c2; + h1 = h1 ^ k1; } - h1 = _toSigned64(h1 ^ bytes.length); - h2 = _toSigned64(h2 ^ bytes.length); + h1 = h1 ^ bytes.length; + h2 = h2 ^ bytes.length; - h1 = _add64(h1, h2); - h2 = _add64(h2, h1); + h1 = h1 + h2; + h2 = h2 + h1; h1 = _fmix64(h1); h2 = _fmix64(h2); - h1 = _add64(h1, h2); - h2 = _add64(h2, h1); + h1 = h1 + h2; + h2 = h2 + h1; return (h1, h2); } Int64 metaStringHash(List bytes, {int encoding = 0}) { - var hash = _absSigned64(murmurHash3X64_128(bytes).$1); - if (hash == 0) { - hash += 0x100; + var hash = + _absSigned64Bits(_int64FromUint64(_murmurHash3X64_128Bits(bytes).$1)); + if (hash.isZero) { + hash = hash + 0x100; } - hash = (BigInt.from(hash) & _metaStringHashMaskBig).toInt(); - return Int64(hash | (encoding & 0xff)); + hash = (hash & _metaStringHashMask) | (encoding & 0xff); + return _int64FromUint64(hash); } Int64 typeDefHeader( @@ -151,17 +157,21 @@ Int64 typeDefHeader( required bool hasFieldsMeta, bool compressed = false, }) { - final hash = _toSigned64(murmurHash3X64_128(bytes).$1 << _typeDefHashShift); - var header = _absSigned64(hash); + final hash = _int64FromUint64( + _murmurHash3X64_128Bits(bytes).$1 << _typeDefHashShift, + ); + var header = _absSigned64Bits(hash); if (compressed) { - header |= _typeDefCompressMetaFlag; + header = header | _typeDefCompressMetaFlag; } if (hasFieldsMeta) { - header |= _typeDefHasFieldsMetaFlag; + header = header | _typeDefHasFieldsMetaFlag; } - header |= - bytes.length > _typeDefMetaSizeMask ? _typeDefMetaSizeMask : bytes.length; - return Int64(_toSigned64(header)); + header = header | + (bytes.length > _typeDefMetaSizeMask + ? _typeDefMetaSizeMask + : bytes.length); + return _int64FromUint64(header); } int schemaHash(TypeDef typeDef) { @@ -180,8 +190,7 @@ int schemaHash(TypeDef typeDef) { .map((buffer) => buffer.toString()) .toList(growable: false) ..sort(); - final hash = murmurHash3X64_128(utf8.encode(parts.join())).$1; - return hash & 0xffffffff; + return _murmurHash3X64_128Bits(utf8.encode(parts.join())).$1.low32; } int _fingerprintTypeId(FieldInfo field) { @@ -207,56 +216,42 @@ int _fingerprintTypeId(FieldInfo field) { } } -int _toSigned64Big(BigInt value) { - final normalized = _u64Big(value); - if ((normalized & _signBit64Big) != BigInt.zero) { - return (normalized - (BigInt.one << 64)).toInt(); - } - return normalized.toInt(); -} - -int _readLongLittleEndian(List bytes, int offset) { - final value = (_signedByte(bytes[offset + 7]) << 56) | - ((bytes[offset + 6] & 0xff) << 48) | - ((bytes[offset + 5] & 0xff) << 40) | - ((bytes[offset + 4] & 0xff) << 32) | - ((bytes[offset + 3] & 0xff) << 24) | - ((bytes[offset + 2] & 0xff) << 16) | - ((bytes[offset + 1] & 0xff) << 8) | - (bytes[offset] & 0xff); - return _toSigned64(value); +Uint64 _readLongLittleEndian(List bytes, int offset) { + final low = (bytes[offset] & 0xff) + + ((bytes[offset + 1] & 0xff) << 8) + + ((bytes[offset + 2] & 0xff) << 16) + + ((bytes[offset + 3] & 0xff) * 0x1000000); + final high = (bytes[offset + 4] & 0xff) + + ((bytes[offset + 5] & 0xff) << 8) + + ((bytes[offset + 6] & 0xff) << 16) + + ((bytes[offset + 7] & 0xff) * 0x1000000); + return Uint64.fromWords(low, high); } -int _signedByte(int value) => value >= 0x80 ? value - 0x100 : value; - -int _rotateLeft64(int value, int shift) { - final normalized = _u64Big(BigInt.from(value)); - return _toSigned64Big((normalized << shift) | (normalized >> (64 - shift))); +Uint64 _rotateLeft64(Uint64 value, int shift) { + return (value << shift) | (value >> (64 - shift)); } -int _unsignedRightShift64(int value, int shift) => - (_u64Big(BigInt.from(value)) >> shift).toInt(); - -int _mul64(int value, BigInt factor) => - _toSigned64Big(BigInt.from(value) * factor); - -int _add64(int left, int right) => - _toSigned64Big(BigInt.from(left) + BigInt.from(right)); - -int _fmix64(int value) { +Uint64 _fmix64(Uint64 value) { var mixed = value; - mixed = _toSigned64(mixed ^ _unsignedRightShift64(mixed, 33)); - mixed = _mul64(mixed, BigInt.parse('ff51afd7ed558ccd', radix: 16)); - mixed = _toSigned64(mixed ^ _unsignedRightShift64(mixed, 33)); - mixed = _mul64(mixed, BigInt.parse('c4ceb9fe1a85ec53', radix: 16)); - mixed = _toSigned64(mixed ^ _unsignedRightShift64(mixed, 33)); + mixed = mixed ^ (mixed >> 33); + mixed = mixed * _fmixC1; + mixed = mixed ^ (mixed >> 33); + mixed = mixed * _fmixC2; + mixed = mixed ^ (mixed >> 33); return mixed; } -BigInt _u64Big(BigInt value) => value & _mask64Big; - -int _toSigned64(int value) { - return _toSigned64Big(BigInt.from(value)); +Uint64 _absSigned64Bits(Int64 value) { + if (!value.isNegative) { + return _uint64FromInt64(value); + } + final negated = -value; + return Uint64.fromWords(negated.low32, negated.high32Unsigned); } -int _absSigned64(int value) => value < 0 ? -value : value; +Int64 _int64FromUint64(Uint64 value) => + Int64.fromWords(value.low32, value.high32Unsigned); + +Uint64 _uint64FromInt64(Int64 value) => + Uint64.fromWords(value.low32, value.high32Unsigned); From 23010282fd83baeafb029ec120a1b455a602742e Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 13:22:09 +0800 Subject: [PATCH 06/14] perf(dart): recover native int64 benchmark throughput --- .../fory/lib/src/codegen/fory_generator.dart | 30 +- .../lib/src/codegen/generated_support.dart | 223 +++++++- .../fory/lib/src/context/read_context.dart | 9 + .../packages/fory/lib/src/meta/type_meta.dart | 9 +- .../fory/lib/src/resolver/type_resolver.dart | 23 + .../serializer/collection_serializers.dart | 56 +- .../lib/src/serializer/struct_serializer.dart | 1 + .../fory/lib/src/types/int64_native.dart | 7 +- .../fory/lib/src/types/int64_web.dart | 60 +- .../fory/lib/src/types/uint64_web.dart | 10 +- .../fory/lib/src/util/int64_codec.dart | 8 + .../fory/lib/src/util/int64_platform.dart | 21 + .../lib/src/util/int64_platform_native.dart | 20 + .../fory/lib/src/util/int64_platform_web.dart | 20 + .../test/int64_uint64_arithmetic_test.dart | 535 ++++++++++++++++++ 15 files changed, 990 insertions(+), 42 deletions(-) create mode 100644 dart/packages/fory/lib/src/util/int64_platform.dart create mode 100644 dart/packages/fory/lib/src/util/int64_platform_native.dart create mode 100644 dart/packages/fory/lib/src/util/int64_platform_web.dart create mode 100644 dart/packages/fory/test/int64_uint64_arithmetic_test.dart diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index dcd92d2791..c4189aaf7f 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -1316,10 +1316,19 @@ GeneratedFieldType( case TypeIds.varInt32: return '$cursorExpression.writeVarInt32(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.int64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeInt64FromInt($valueExpression)'; + } return '$cursorExpression.writeInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.varInt64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeVarInt64FromInt($valueExpression)'; + } return '$cursorExpression.writeVarInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.taggedInt64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeTaggedInt64FromInt($valueExpression)'; + } return '$cursorExpression.writeTaggedInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.uint8: return '$cursorExpression.writeUint8(${_directGeneratedScalarExpression(field, valueExpression)})'; @@ -1330,10 +1339,19 @@ GeneratedFieldType( case TypeIds.varUint32: return '$cursorExpression.writeVarUint32(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.uint64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeUint64FromInt($valueExpression)'; + } return '$cursorExpression.writeUint64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.varUint64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeVarUint64FromInt($valueExpression)'; + } return '$cursorExpression.writeVarUint64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.taggedUint64: + if (field.type.isDartCoreInt) { + return '$cursorExpression.writeTaggedUint64FromInt($valueExpression)'; + } return '$cursorExpression.writeTaggedUint64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.float16: return '$cursorExpression.writeFloat16($valueExpression)'; @@ -1500,15 +1518,15 @@ GeneratedFieldType( : 'Int32($cursorExpression.readVarInt32())'; case TypeIds.int64: return field.type.isDartCoreInt - ? '$cursorExpression.readInt64().toInt()' + ? '$cursorExpression.readInt64AsInt()' : '$cursorExpression.readInt64()'; case TypeIds.varInt64: return field.type.isDartCoreInt - ? '$cursorExpression.readVarInt64().toInt()' + ? '$cursorExpression.readVarInt64AsInt()' : '$cursorExpression.readVarInt64()'; case TypeIds.taggedInt64: return field.type.isDartCoreInt - ? '$cursorExpression.readTaggedInt64().toInt()' + ? '$cursorExpression.readTaggedInt64AsInt()' : '$cursorExpression.readTaggedInt64()'; case TypeIds.uint8: return field.type.isDartCoreInt @@ -1528,15 +1546,15 @@ GeneratedFieldType( : 'Uint32($cursorExpression.readVarUint32())'; case TypeIds.uint64: return field.type.isDartCoreInt - ? '$cursorExpression.readUint64().toInt()' + ? '$cursorExpression.readUint64AsInt()' : '$cursorExpression.readUint64()'; case TypeIds.varUint64: return field.type.isDartCoreInt - ? '$cursorExpression.readVarUint64().toInt()' + ? '$cursorExpression.readVarUint64AsInt()' : '$cursorExpression.readVarUint64()'; case TypeIds.taggedUint64: return field.type.isDartCoreInt - ? '$cursorExpression.readTaggedUint64().toInt()' + ? '$cursorExpression.readTaggedUint64AsInt()' : '$cursorExpression.readTaggedUint64()'; case TypeIds.float16: return '$cursorExpression.readFloat16()'; diff --git a/dart/packages/fory/lib/src/codegen/generated_support.dart b/dart/packages/fory/lib/src/codegen/generated_support.dart index 0e4e462e6a..023bd5f3b2 100644 --- a/dart/packages/fory/lib/src/codegen/generated_support.dart +++ b/dart/packages/fory/lib/src/codegen/generated_support.dart @@ -37,6 +37,7 @@ import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/time_serializers.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; import 'package:fory/src/util/int64_codec.dart'; +import 'package:fory/src/util/int64_platform.dart'; @internal final class GeneratedWriteCursor { @@ -66,69 +67,103 @@ final class GeneratedWriteCursor { bufferSetWriterIndex(_buffer, _offset); } + @pragma('vm:prefer-inline') void writeBool(bool value) { _bytes[_offset] = value ? 1 : 0; _offset += 1; } + @pragma('vm:prefer-inline') void writeByte(int value) { _view.setInt8(_offset, value); _offset += 1; } + @pragma('vm:prefer-inline') void writeUint8(int value) { _view.setUint8(_offset, value); _offset += 1; } + @pragma('vm:prefer-inline') void writeInt16(int value) { _view.setInt16(_offset, value, Endian.little); _offset += 2; } + @pragma('vm:prefer-inline') void writeUint16(int value) { _view.setUint16(_offset, value, Endian.little); _offset += 2; } + @pragma('vm:prefer-inline') void writeInt32(int value) { _view.setInt32(_offset, value, Endian.little); _offset += 4; } + @pragma('vm:prefer-inline') void writeUint32(int value) { _view.setUint32(_offset, value, Endian.little); _offset += 4; } + @pragma('vm:prefer-inline') void writeInt64(Int64 value) { writeInt64LittleEndian(_view, _offset, value); _offset += 8; } + @pragma('vm:prefer-inline') + void writeInt64FromInt(int value) { + if (useNativeInt64FastPath) { + _view.setInt64(_offset, value, Endian.little); + _offset += 8; + return; + } + writeInt64(Int64(value)); + } + + @pragma('vm:prefer-inline') void writeUint64(Uint64 value) { writeUint64LittleEndian(_view, _offset, value); _offset += 8; } + @pragma('vm:prefer-inline') + void writeUint64FromInt(int value) { + if (useNativeInt64FastPath) { + _view.setUint64(_offset, value, Endian.little); + _offset += 8; + return; + } + writeUint64(Uint64(value)); + } + + @pragma('vm:prefer-inline') void writeFloat16(Float16 value) { writeUint16(value.toBits()); } + @pragma('vm:prefer-inline') void writeBfloat16(Bfloat16 value) { writeUint16(value.toBits()); } + @pragma('vm:prefer-inline') void writeFloat32(double value) { _view.setFloat32(_offset, value, Endian.little); _offset += 4; } + @pragma('vm:prefer-inline') void writeFloat64(double value) { _view.setFloat64(_offset, value, Endian.little); _offset += 8; } + @pragma('vm:prefer-inline') void writeVarUint32(int value) { var remaining = value; while (remaining >= 0x80) { @@ -140,10 +175,15 @@ final class GeneratedWriteCursor { _offset += 1; } + @pragma('vm:prefer-inline') void writeVarInt32(int value) { - writeVarUint32(((value << 1) ^ (value >> 31)).toUnsigned(32)); + final encoded = (value << 1) ^ (value >> 31); + writeVarUint32( + useNativeInt64FastPath ? encoded : encoded.toUnsigned(32), + ); } + @pragma('vm:prefer-inline') void writeVarUint64(Uint64 value) { writeVarUint64Bytes(value, (byte) { _bytes[_offset] = byte; @@ -151,10 +191,30 @@ final class GeneratedWriteCursor { }); } + @pragma('vm:prefer-inline') + void writeVarUint64FromInt(int value) { + if (useNativeInt64FastPath) { + _writeVarUint64Int(value); + return; + } + writeVarUint64(Uint64(value)); + } + + @pragma('vm:prefer-inline') void writeVarInt64(Int64 value) { writeVarUint64(zigZagEncodeInt64(value)); } + @pragma('vm:prefer-inline') + void writeVarInt64FromInt(int value) { + if (useNativeInt64FastPath) { + _writeVarUint64Int((value << 1) ^ (value >> 63)); + return; + } + writeVarInt64(Int64(value)); + } + + @pragma('vm:prefer-inline') void writeTaggedInt64(Int64 value) { if (value >= -0x40000000 && value <= 0x3fffffff) { writeInt32((value.toInt() << 1).toSigned(32)); @@ -164,6 +224,17 @@ final class GeneratedWriteCursor { writeInt64(value); } + @pragma('vm:prefer-inline') + void writeTaggedInt64FromInt(int value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64FromInt(value); + } + + @pragma('vm:prefer-inline') void writeTaggedUint64(Uint64 value) { if (value >= 0 && value <= 0x7fffffff) { writeInt32(value.toInt() << 1); @@ -172,6 +243,34 @@ final class GeneratedWriteCursor { writeUint8(0x01); writeUint64(value); } + + @pragma('vm:prefer-inline') + void writeTaggedUint64FromInt(int value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value << 1); + return; + } + writeUint8(0x01); + writeUint64FromInt(value); + } + + @pragma('vm:prefer-inline') + void _writeVarUint64Int(int value) { + var remaining = value; + for (var index = 0; index < 8; index += 1) { + final chunk = remaining & 0x7f; + remaining = remaining >>> 7; + if (remaining == 0) { + _bytes[_offset] = chunk; + _offset += 1; + return; + } + _bytes[_offset] = chunk | 0x80; + _offset += 1; + } + _bytes[_offset] = remaining & 0xff; + _offset += 1; + } } @internal @@ -198,72 +297,106 @@ final class GeneratedReadCursor { bufferSetReaderIndex(_buffer, _offset); } + @pragma('vm:prefer-inline') bool readBool() => readUint8() != 0; + @pragma('vm:prefer-inline') int readByte() { final value = _view.getInt8(_offset); _offset += 1; return value; } + @pragma('vm:prefer-inline') int readUint8() { final value = _view.getUint8(_offset); _offset += 1; return value; } + @pragma('vm:prefer-inline') int readInt16() { final value = _view.getInt16(_offset, Endian.little); _offset += 2; return value; } + @pragma('vm:prefer-inline') int readUint16() { final value = _view.getUint16(_offset, Endian.little); _offset += 2; return value; } + @pragma('vm:prefer-inline') int readInt32() { final value = _view.getInt32(_offset, Endian.little); _offset += 4; return value; } + @pragma('vm:prefer-inline') int readUint32() { final value = _view.getUint32(_offset, Endian.little); _offset += 4; return value; } + @pragma('vm:prefer-inline') Int64 readInt64() { final value = readInt64LittleEndian(_view, _offset); _offset += 8; return value; } + @pragma('vm:prefer-inline') + int readInt64AsInt() { + if (useNativeInt64FastPath) { + final value = _view.getInt64(_offset, Endian.little); + _offset += 8; + return value; + } + return readInt64().toInt(); + } + + @pragma('vm:prefer-inline') Uint64 readUint64() { final value = readUint64LittleEndian(_view, _offset); _offset += 8; return value; } + @pragma('vm:prefer-inline') + int readUint64AsInt() { + if (useNativeInt64FastPath) { + final value = _view.getUint64(_offset, Endian.little); + _offset += 8; + return value; + } + return readUint64().toInt(); + } + + @pragma('vm:prefer-inline') Float16 readFloat16() => Float16.fromBits(readUint16()); + @pragma('vm:prefer-inline') Bfloat16 readBfloat16() => Bfloat16.fromBits(readUint16()); + @pragma('vm:prefer-inline') double readFloat32() { final value = _view.getFloat32(_offset, Endian.little); _offset += 4; return value; } + @pragma('vm:prefer-inline') double readFloat64() { final value = _view.getFloat64(_offset, Endian.little); _offset += 8; return value; } + @pragma('vm:prefer-inline') int readVarUint32() { var shift = 0; var result = 0; @@ -277,19 +410,41 @@ final class GeneratedReadCursor { } } + @pragma('vm:prefer-inline') int readVarInt32() { final value = readVarUint32(); - return ((value >>> 1) ^ -(value & 1)).toSigned(32); + final decoded = (value >>> 1) ^ -(value & 1); + return useNativeInt64FastPath ? decoded : decoded.toSigned(32); } + @pragma('vm:prefer-inline') Uint64 readVarUint64() { return readVarUint64Bytes(readUint8); } + @pragma('vm:prefer-inline') + int readVarUint64AsInt() { + if (useNativeInt64FastPath) { + return _readVarUint64Int(); + } + return readVarUint64().toInt(); + } + + @pragma('vm:prefer-inline') Int64 readVarInt64() { return zigZagDecodeInt64(readVarUint64()); } + @pragma('vm:prefer-inline') + int readVarInt64AsInt() { + if (useNativeInt64FastPath) { + final encoded = _readVarUint64Int(); + return (encoded >>> 1) ^ -(encoded & 1); + } + return readVarInt64().toInt(); + } + + @pragma('vm:prefer-inline') Int64 readTaggedInt64() { final readIndex = _offset; final first = _view.getInt32(readIndex, Endian.little); @@ -302,6 +457,23 @@ final class GeneratedReadCursor { return value; } + @pragma('vm:prefer-inline') + int readTaggedInt64AsInt() { + if (useNativeInt64FastPath) { + final readIndex = _offset; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >> 1; + } + final value = _view.getInt64(readIndex + 1, Endian.little); + _offset = readIndex + 9; + return value; + } + return readTaggedInt64().toInt(); + } + + @pragma('vm:prefer-inline') Uint64 readTaggedUint64() { final readIndex = _offset; final first = _view.getUint32(readIndex, Endian.little); @@ -313,6 +485,37 @@ final class GeneratedReadCursor { _offset = readIndex + 9; return value; } + + @pragma('vm:prefer-inline') + int readTaggedUint64AsInt() { + if (useNativeInt64FastPath) { + final readIndex = _offset; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >>> 1; + } + final value = _view.getUint64(readIndex + 1, Endian.little); + _offset = readIndex + 9; + return value; + } + return readTaggedUint64().toInt(); + } + + @pragma('vm:prefer-inline') + int _readVarUint64Int() { + var shift = 0; + var result = 0; + while (shift < 56) { + final byte = readUint8(); + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + return result | (readUint8() << 56); + } } @internal @@ -675,6 +878,7 @@ void writeGeneratedStructFieldInfoValue( } @internal +@pragma('vm:prefer-inline') Object? readGeneratedStructFieldInfoValue( ReadContext context, GeneratedStructFieldInfo field, [ @@ -695,15 +899,14 @@ Object? readGeneratedStructFieldInfoValue( if (fieldUsesDeclaredType(context.typeResolver, field)) { return context.readResolvedValue(resolved, fieldType); } - final actualResolved = context.readTypeMetaValue( - resolved.isNamed ? resolved : null, - ); + final actualResolved = context.readTypeMetaValue(resolved); return context.readResolvedValue(actualResolved, fieldType); } return readFieldValue(context, field, fallback); } @internal +@pragma('vm:prefer-inline') Object? readGeneratedStructDeclaredValue( ReadContext context, GeneratedStructFieldInfo field, @@ -712,13 +915,12 @@ Object? readGeneratedStructDeclaredValue( if (fieldUsesDeclaredType(context.typeResolver, field)) { return context.readResolvedValue(resolved, field.fieldType); } - final actualResolved = context.readTypeMetaValue( - resolved.isNamed ? resolved : null, - ); + final actualResolved = context.readTypeMetaValue(resolved); return context.readResolvedValue(actualResolved, field.fieldType); } @internal +@pragma('vm:prefer-inline') Object readGeneratedStructDirectValue( ReadContext context, GeneratedStructFieldInfo field, @@ -728,7 +930,7 @@ Object readGeneratedStructDirectValue( if (fieldUsesDeclaredType(context.typeResolver, field)) { resolved = declared; } else { - resolved = context.readTypeMetaValue(declared.isNamed ? declared : null); + resolved = context.readTypeMetaValue(declared); } context.increaseDepth(); final value = resolved.structSerializer!.readValue(context, resolved); @@ -737,6 +939,7 @@ Object readGeneratedStructDirectValue( } @internal +@pragma('vm:prefer-inline') List readGeneratedDirectListValue( ReadContext context, GeneratedStructFieldInfo field, @@ -757,6 +960,7 @@ List readGeneratedDirectListValue( } @internal +@pragma('vm:prefer-inline') Set readGeneratedDirectSetValue( ReadContext context, GeneratedStructFieldInfo field, @@ -777,6 +981,7 @@ Set readGeneratedDirectSetValue( } @internal +@pragma('vm:prefer-inline') Map readGeneratedDirectMapValue( ReadContext context, GeneratedStructFieldInfo field, diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart index 821f574077..55476e0f0f 100644 --- a/dart/packages/fory/lib/src/context/read_context.dart +++ b/dart/packages/fory/lib/src/context/read_context.dart @@ -99,12 +99,14 @@ final class ReadContext { } @internal + @pragma('vm:prefer-inline') TypeInfo readTypeMetaValue([ TypeInfo? expectedNamedType, ]) => _readTypeMeta(expectedNamedType); @internal + @pragma('vm:prefer-inline') Object? readSerializerPayload( Serializer serializer, TypeInfo resolved, {required bool hasCurrentPreservedRef}) { @@ -131,6 +133,7 @@ final class ReadContext { int get depth => _depth; /// Records entry into one more nested read frame. + @pragma('vm:prefer-inline') void increaseDepth() { _depth += 1; if (_depth > config.maxDepth) { @@ -139,6 +142,7 @@ final class ReadContext { } /// Records exit from a nested read frame. + @pragma('vm:prefer-inline') void decreaseDepth() { _depth -= 1; } @@ -192,11 +196,13 @@ final class ReadContext { Uint64 readTaggedUint64() => _buffer.readTaggedUint64(); /// Binds [value] to the most recently preserved Ref slot. + @pragma('vm:prefer-inline') void reference(Object? value) { _refReader.reference(value); } /// Reads a non-null string payload without ref/null handling. + @pragma('vm:prefer-inline') String readString() => StringSerializer.readPayload(this); /// Reads a ref-or-null header and resolves back-references immediately. @@ -277,6 +283,7 @@ final class ReadContext { PrimitiveSerializer.readPayload(this, typeId); @internal + @pragma('vm:prefer-inline') Object? readResolvedValue(TypeInfo resolved, FieldType? declaredFieldType, {bool hasPreservedRef = false}) { if (!_tracksDepth(resolved)) { @@ -394,6 +401,7 @@ final class ReadContext { } } + @pragma('vm:prefer-inline') TypeInfo _readTypeMeta([ TypeInfo? expectedNamedType, ]) { @@ -405,6 +413,7 @@ final class ReadContext { ); } + @pragma('vm:prefer-inline') bool _tracksDepth(TypeInfo resolved) { if (TypeIds.isContainer(resolved.typeId)) { return true; diff --git a/dart/packages/fory/lib/src/meta/type_meta.dart b/dart/packages/fory/lib/src/meta/type_meta.dart index 50fb5d33e8..3941f8c27e 100644 --- a/dart/packages/fory/lib/src/meta/type_meta.dart +++ b/dart/packages/fory/lib/src/meta/type_meta.dart @@ -57,14 +57,16 @@ final class TypeHeader { const TypeHeader(this.value); + @pragma('vm:prefer-inline') int readMetaSize(Buffer buffer) { - final lowBits = (value & 0xff).toInt(); + final lowBits = value.low32 & 0xff; if (lowBits == 0xff) { return 0xff + buffer.readVarUint32Small14(); } return lowBits; } + @pragma('vm:prefer-inline') void skipRemaining(Buffer buffer) { buffer.skip(readMetaSize(buffer)); } @@ -73,10 +75,12 @@ final class TypeHeader { final class ParsedTypeMetaCache { static const int maxEntries = 8192; - final LinkedHashMap _entries = LinkedHashMap(); + final LinkedHashMap _entries = + LinkedHashMap(); Int64? _lastHeader; TypeInfo? _lastResolved; + @pragma('vm:prefer-inline') TypeInfo? lookup(TypeHeader header) { if (_lastHeader == header.value) { return _lastResolved; @@ -89,6 +93,7 @@ final class ParsedTypeMetaCache { return resolved; } + @pragma('vm:prefer-inline') void remember(TypeHeader header, TypeInfo resolved) { if (!_entries.containsKey(header.value) && _entries.length >= maxEntries) { _entries.remove(_entries.keys.first); diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index 5d2b215df3..981f7fb3a9 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -629,6 +629,7 @@ final class TypeResolver { ); } + @pragma('vm:prefer-inline') TypeInfo readTypeMeta( Buffer buffer, { TypeInfo? expectedNamedType, @@ -653,6 +654,7 @@ final class TypeResolver { readTypeDef: () => _readTypeDef( buffer, sharedTypes: sharedTypes, + expectedType: expectedNamedType, ), readPackageMetaString: ([expected]) => metaStringReader.readMetaString(buffer, expected), @@ -854,9 +856,11 @@ final class TypeResolver { return fieldType.ref ? TypeIds.unknown : fieldType.typeId; } + @pragma('vm:prefer-inline') WireTypeMeta _readTypeDef( Buffer buffer, { required List sharedTypes, + TypeInfo? expectedType, }) { final marker = buffer.readVarUint32Small14(); final isRef = (marker & 1) == 1; @@ -865,6 +869,12 @@ final class TypeResolver { return wireTypeMetaForResolved(sharedTypes[index]); } final header = TypeHeader(buffer.readInt64()); + final expectedTypeDef = expectedType?.typeDef; + if (expectedTypeDef != null && expectedTypeDef.header == header.value) { + header.skipRemaining(buffer); + sharedTypes.add(expectedType!); + return wireTypeMetaForResolved(expectedType); + } final cached = _parsedTypeMetaCache.lookup(header); if (cached != null) { header.skipRemaining(buffer); @@ -1434,6 +1444,19 @@ final class TypeResolver { } Type _builtinTypeForFieldType(FieldType fieldType) { + switch (fieldType.typeId) { + case TypeIds.int64: + case TypeIds.varInt64: + case TypeIds.taggedInt64: + case TypeIds.uint64: + case TypeIds.varUint64: + case TypeIds.taggedUint64: + case TypeIds.int64Array: + case TypeIds.uint64Array: + break; + default: + return fieldType.type; + } final declaredTypeName = fieldType.declaredTypeName; if (declaredTypeName != null) { if (_matchesDeclaredTypeName(declaredTypeName, 'Int64')) { diff --git a/dart/packages/fory/lib/src/serializer/collection_serializers.dart b/dart/packages/fory/lib/src/serializer/collection_serializers.dart index f521a2cd01..f65feb7236 100644 --- a/dart/packages/fory/lib/src/serializer/collection_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/collection_serializers.dart @@ -404,6 +404,7 @@ final class SetSerializer extends Serializer { const ListSerializer listSerializer = ListSerializer(); const SetSerializer setSerializer = SetSerializer(); +@pragma('vm:prefer-inline') List readTypedListPayload( ReadContext context, FieldType? elementFieldType, @@ -416,6 +417,45 @@ List readTypedListPayload( if (state.tracksDepth) { context.increaseDepth(); } + final directTypeInfo = state.declaredTypeInfo ?? state.sameTypeInfo; + if (directTypeInfo != null && !state.trackRef && !state.hasNull) { + final directFieldType = + state.declaredTypeInfo != null ? state.elementFieldType : null; + if (directTypeInfo.type == T && + directTypeInfo.kind == RegistrationKind.struct) { + final structSerializer = directTypeInfo.structSerializer!; + final result = List.generate( + state.size, + (_) => structSerializer.readValue(context, directTypeInfo) as T, + growable: false, + ); + if (state.tracksDepth) { + context.decreaseDepth(); + } + return result; + } + if (directTypeInfo.type == T && directTypeInfo.typeId == TypeIds.string) { + final result = List.generate( + state.size, + (_) => StringSerializer.readPayload(context) as T, + growable: false, + ); + if (state.tracksDepth) { + context.decreaseDepth(); + } + return result; + } + final result = List.generate( + state.size, + (_) => + convert(readTypeInfoValue(context, directTypeInfo, directFieldType)), + growable: false, + ); + if (state.tracksDepth) { + context.decreaseDepth(); + } + return result; + } final result = List.generate( state.size, (_) => convert(_readPreparedListItem(context, state)), @@ -578,6 +618,7 @@ final class _PreparedListRead { }); } +@pragma('vm:prefer-inline') _PreparedListRead _prepareListRead( ReadContext context, FieldType? elementFieldType, @@ -606,13 +647,16 @@ _PreparedListRead _prepareListRead( 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), - ) + final needsExpectedElementType = elementFieldType != null && + (usesDeclaredType || + (sameType && TypeIds.isUserType(elementFieldType.typeId))); + final expectedElementTypeInfo = needsExpectedElementType + ? context.typeResolver.tryResolveFieldType(elementFieldType) + : null; + final declaredTypeInfo = usesDeclaredType ? expectedElementTypeInfo : null; + final sameTypeInfo = (!usesDeclaredType && sameType) + ? context.readTypeMetaValue(expectedElementTypeInfo) : null; - final sameTypeInfo = - (!usesDeclaredType && sameType) ? context.readTypeMetaValue() : null; final tracksDepth = (declaredTypeInfo != null && tracksNestedPayloadDepth(declaredTypeInfo)) || (sameTypeInfo != null && tracksNestedPayloadDepth(sameTypeInfo)); diff --git a/dart/packages/fory/lib/src/serializer/struct_serializer.dart b/dart/packages/fory/lib/src/serializer/struct_serializer.dart index f0b4df1c34..d7167b57dd 100644 --- a/dart/packages/fory/lib/src/serializer/struct_serializer.dart +++ b/dart/packages/fory/lib/src/serializer/struct_serializer.dart @@ -108,6 +108,7 @@ final class StructSerializer extends Serializer { internal.structWriteSlots = previousCompatibleFields; } + @pragma('vm:prefer-inline') Object readValue( ReadContext context, TypeInfo resolved, { diff --git a/dart/packages/fory/lib/src/types/int64_native.dart b/dart/packages/fory/lib/src/types/int64_native.dart index 4decbb9cdf..1c7f148c62 100644 --- a/dart/packages/fory/lib/src/types/int64_native.dart +++ b/dart/packages/fory/lib/src/types/int64_native.dart @@ -28,9 +28,8 @@ extension type Int64._(int _value) implements int { factory Int64.fromBigInt(BigInt value) => Int64._(_normalizeSigned64(value)); /// Creates a signed 64-bit value from little-endian 32-bit words. - factory Int64.fromWords(int low32, int high32) => - Int64._(((high32 & 0xffffffff) << 32 | (low32 & 0xffffffff)) - .toSigned(64)); + factory Int64.fromWords(int low32, int high32) => Int64._( + ((high32 & 0xffffffff) << 32 | (low32 & 0xffffffff)).toSigned(64)); /// Parses a hexadecimal two's-complement payload. factory Int64.parseHex(String value) => @@ -151,6 +150,8 @@ extension type Int64._(int _value) implements int { Int64 operator >>(int shift) => Int64(_value >> shift); + Int64 operator >>>(int shift) => Int64(_value >>> shift); + bool operator <(Object other) => switch (other) { int otherValue => _value < otherValue, _ => throw ArgumentError.value( diff --git a/dart/packages/fory/lib/src/types/int64_web.dart b/dart/packages/fory/lib/src/types/int64_web.dart index b50a5493bc..417cc68f3e 100644 --- a/dart/packages/fory/lib/src/types/int64_web.dart +++ b/dart/packages/fory/lib/src/types/int64_web.dart @@ -27,12 +27,13 @@ const int _int64HighBits = 20; const int _int64HighMask = (1 << _int64HighBits) - 1; const int _int64HighBase = 1 << _int64HighBits; const int _int64HighSignBit = 1 << (_int64HighBits - 1); +const int _int64SafeHighMax = 0x1ff; +const int _int64SafeHighMin = -0x200; +const int _int64HighValueBase = 17592186044416; const int _int64WordBase = 0x100000000; final BigInt _int64Mask64Big = (BigInt.one << 64) - BigInt.one; final BigInt _int64Mask32Big = (BigInt.one << 32) - BigInt.one; final BigInt _int64SignBit64Big = BigInt.one << 63; -final BigInt _int64SafeMinBig = BigInt.from(-9007199254740991); -final BigInt _int64SafeMaxBig = BigInt.from(9007199254740991); /// Signed 64-bit integer wrapper used by the xlang type system on web builds. final class Int64 implements Comparable { @@ -104,11 +105,12 @@ final class Int64 implements Comparable { /// Returns this value as a Dart [int] when it is JS-safe. int toInt() { - final exact = toBigInt(); - if (exact < _int64SafeMinBig || exact > _int64SafeMaxBig) { - throw StateError('Int64 value $exact is not a JS-safe int.'); + if (_h > _int64SafeHighMax || + _h < _int64SafeHighMin || + (_h == _int64SafeHighMin && _m == 0 && _l == 0)) { + throw StateError('Int64 value ${toBigInt()} is not a JS-safe int.'); } - return exact.toInt(); + return _h * _int64HighValueBase + _m * _int64SegmentBase + _l; } @override @@ -159,9 +161,9 @@ final class Int64 implements Comparable { Int64 operator %(Object other) => switch (other) { int otherValue => - Int64.fromBigInt(toBigInt().remainder(BigInt.from(otherValue))), + Int64.fromBigInt(toBigInt() % BigInt.from(otherValue)), Int64 otherValue => - Int64.fromBigInt(toBigInt().remainder(otherValue.toBigInt())), + Int64.fromBigInt(toBigInt() % otherValue.toBigInt()), _ => throw ArgumentError.value( other, 'other', 'Expected an int or Int64.'), }; @@ -246,9 +248,7 @@ final class Int64 implements Comparable { return Int64._parts( ((_l >>> shift) | ((_m & _int64LowBitsMask(shift)) << (22 - shift))) & _int64SegmentMask, - ((_m >>> shift) | - (((_h & _int64HighMask) & _int64LowBitsMask(shift)) << - (22 - shift))) & + ((_m >>> shift) | ((_h & _int64LowBitsMask(shift)) << (22 - shift))) & _int64SegmentMask, _normalizeInt64High(_h >> shift), ); @@ -272,6 +272,17 @@ final class Int64 implements Comparable { ); } + Int64 operator >>>(int shift) { + RangeError.checkNotNegative(shift, 'shift'); + if (shift == 0) { + return this; + } + if (shift >= 64) { + return Int64(0); + } + return _logicalShiftRight(shift); + } + bool operator <(Object other) => switch (other) { int otherValue => compareTo(Int64(otherValue)) < 0, Int64 otherValue => compareTo(otherValue) < 0, @@ -358,6 +369,33 @@ final class Int64 implements Comparable { _m ^ other._m, _normalizeInt64High(_h ^ other._h), ); + + Int64 _logicalShiftRight(int shift) { + final highBits = _h & _int64HighMask; + if (shift < 22) { + return Int64._parts( + ((_l >>> shift) | ((_m & _int64LowBitsMask(shift)) << (22 - shift))) & + _int64SegmentMask, + ((_m >>> shift) | + ((highBits & _int64LowBitsMask(shift)) << (22 - shift))) & + _int64SegmentMask, + highBits >>> shift, + ); + } + if (shift < 44) { + final segmentShift = shift - 22; + return Int64._parts( + ((_m >>> segmentShift) | + ((highBits & _int64LowBitsMask(segmentShift)) << + (22 - segmentShift))) & + _int64SegmentMask, + highBits >>> segmentShift, + 0, + ); + } + final segmentShift = shift - 44; + return Int64._parts(highBits >>> segmentShift, 0, 0); + } } /// Fixed-length contiguous storage for [Int64] values on web builds. diff --git a/dart/packages/fory/lib/src/types/uint64_web.dart b/dart/packages/fory/lib/src/types/uint64_web.dart index f3bed35f88..0397d2cac4 100644 --- a/dart/packages/fory/lib/src/types/uint64_web.dart +++ b/dart/packages/fory/lib/src/types/uint64_web.dart @@ -25,10 +25,11 @@ const int _uint64SegmentMask = (1 << _uint64SegmentBits) - 1; const int _uint64SegmentBase = 1 << _uint64SegmentBits; const int _uint64HighBits = 20; const int _uint64HighMask = (1 << _uint64HighBits) - 1; +const int _uint64SafeHighMax = 0x1ff; +const int _uint64HighValueBase = 17592186044416; const int _uint64WordBase = 0x100000000; final BigInt _uint64Mask64Big = (BigInt.one << 64) - BigInt.one; final BigInt _uint64Mask32Big = (BigInt.one << 32) - BigInt.one; -final BigInt _uint64SafeMaxBig = BigInt.from(9007199254740991); /// Unsigned 64-bit integer wrapper used by the xlang type system on web builds. final class Uint64 implements Comparable { @@ -85,11 +86,10 @@ final class Uint64 implements Comparable { /// Returns this value as a Dart [int] when it is JS-safe. int toInt() { - final exact = toBigInt(); - if (exact > _uint64SafeMaxBig) { - throw StateError('Uint64 value $exact is not a JS-safe int.'); + if (_h > _uint64SafeHighMax) { + throw StateError('Uint64 value ${toBigInt()} is not a JS-safe int.'); } - return exact.toInt(); + return _h * _uint64HighValueBase + _m * _uint64SegmentBase + _l; } @override diff --git a/dart/packages/fory/lib/src/util/int64_codec.dart b/dart/packages/fory/lib/src/util/int64_codec.dart index b31e0132f6..22b468ea3b 100644 --- a/dart/packages/fory/lib/src/util/int64_codec.dart +++ b/dart/packages/fory/lib/src/util/int64_codec.dart @@ -21,13 +21,21 @@ import 'dart:typed_data'; import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/uint64.dart'; +import 'package:fory/src/util/int64_platform.dart'; void writeInt64LittleEndian(ByteData view, int offset, Int64 value) { + if (useNativeInt64FastPath) { + view.setInt64(offset, value.toInt(), Endian.little); + return; + } view.setUint32(offset, value.low32, Endian.little); view.setUint32(offset + 4, value.high32Unsigned, Endian.little); } Int64 readInt64LittleEndian(ByteData view, int offset) { + if (useNativeInt64FastPath) { + return Int64(view.getInt64(offset, Endian.little)); + } return Int64.fromWords( view.getUint32(offset, Endian.little), view.getInt32(offset + 4, Endian.little), diff --git a/dart/packages/fory/lib/src/util/int64_platform.dart b/dart/packages/fory/lib/src/util/int64_platform.dart new file mode 100644 index 0000000000..7b9d0473ea --- /dev/null +++ b/dart/packages/fory/lib/src/util/int64_platform.dart @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export 'int64_platform_native.dart' + if (dart.library.js_interop) 'int64_platform_web.dart'; diff --git a/dart/packages/fory/lib/src/util/int64_platform_native.dart b/dart/packages/fory/lib/src/util/int64_platform_native.dart new file mode 100644 index 0000000000..2033e28cc0 --- /dev/null +++ b/dart/packages/fory/lib/src/util/int64_platform_native.dart @@ -0,0 +1,20 @@ +/* + * 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. + */ + +const bool useNativeInt64FastPath = true; diff --git a/dart/packages/fory/lib/src/util/int64_platform_web.dart b/dart/packages/fory/lib/src/util/int64_platform_web.dart new file mode 100644 index 0000000000..a492ddc52e --- /dev/null +++ b/dart/packages/fory/lib/src/util/int64_platform_web.dart @@ -0,0 +1,20 @@ +/* + * 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. + */ + +const bool useNativeInt64FastPath = false; diff --git a/dart/packages/fory/test/int64_uint64_arithmetic_test.dart b/dart/packages/fory/test/int64_uint64_arithmetic_test.dart new file mode 100644 index 0000000000..9224a0844e --- /dev/null +++ b/dart/packages/fory/test/int64_uint64_arithmetic_test.dart @@ -0,0 +1,535 @@ +/* + * 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 'package:fory/fory.dart'; +import 'package:test/test.dart'; + +const bool _isWeb = bool.fromEnvironment('dart.library.js_util'); + +final BigInt _mask32 = (BigInt.one << 32) - BigInt.one; +final BigInt _mask64 = (BigInt.one << 64) - BigInt.one; +final BigInt _signBit64 = BigInt.one << 63; +final BigInt _jsSafeMax = (BigInt.one << 53) - BigInt.one; +final BigInt _jsSafeMin = -_jsSafeMax; + +BigInt _normalizeSigned64(BigInt value) { + final masked = value & _mask64; + return masked & _signBit64 == BigInt.zero + ? masked + : masked - (BigInt.one << 64); +} + +BigInt _normalizeUnsigned64(BigInt value) => value & _mask64; + +bool _isJsSafeSigned64(BigInt value) => + value >= _jsSafeMin && value <= _jsSafeMax; + +bool _isJsSafeUnsigned64(BigInt value) => + value >= BigInt.zero && value <= _jsSafeMax; + +void _expectInt64State( + Int64 actual, + BigInt expected, { + String? reason, +}) { + final normalized = _normalizeSigned64(expected); + final low32 = (normalized & _mask32).toInt(); + final high32 = ((normalized >> 32) & _mask32).toInt(); + + expect(actual.toBigInt(), equals(normalized), reason: reason); + expect(actual.low32, equals(low32), reason: reason); + expect(actual.high32Unsigned, equals(high32), reason: reason); + expect(actual.high32Signed, equals(high32.toSigned(32)), reason: reason); + expect(actual.isZero, equals(normalized == BigInt.zero), reason: reason); + expect(actual.isNegative, equals(normalized.isNegative), reason: reason); + + if (_isWeb && !_isJsSafeSigned64(normalized)) { + expect(() => actual.toInt(), throwsStateError, reason: reason); + return; + } + expect(BigInt.from(actual.toInt()), equals(normalized), reason: reason); +} + +void _expectUint64State( + Uint64 actual, + BigInt expected, { + String? reason, +}) { + final normalized = _normalizeUnsigned64(expected); + final low32 = (normalized & _mask32).toInt(); + final high32 = ((normalized >> 32) & _mask32).toInt(); + + expect(actual.toBigInt(), equals(normalized), reason: reason); + expect(actual.low32, equals(low32), reason: reason); + expect(actual.high32Unsigned, equals(high32), reason: reason); + expect(actual.isZero, equals(normalized == BigInt.zero), reason: reason); + + if (_isWeb) { + if (!_isJsSafeUnsigned64(normalized)) { + expect(() => actual.toInt(), throwsStateError, reason: reason); + return; + } + } else if (normalized >= _signBit64) { + expect(() => actual.toInt(), throwsStateError, reason: reason); + return; + } + + expect(BigInt.from(actual.toInt()), equals(normalized), reason: reason); +} + +void main() { + group('Int64', () { + test('constructors normalize words and hex edge cases', () { + final cases = <({String name, Int64 value, BigInt expected})>[ + (name: 'zero', value: Int64.fromWords(0, 0), expected: BigInt.zero), + (name: 'one', value: Int64.fromWords(1, 0), expected: BigInt.one), + ( + name: '2^22', + value: Int64.fromWords(0x00400000, 0), + expected: BigInt.one << 22, + ), + ( + name: '2^23', + value: Int64.fromWords(0x00800000, 0), + expected: BigInt.one << 23, + ), + ( + name: '2^31', + value: Int64.fromWords(0x80000000, 0), + expected: BigInt.one << 31, + ), + ( + name: '2^32', + value: Int64.fromWords(0, 1), + expected: BigInt.one << 32, + ), + ( + name: '2^43', + value: Int64.fromWords(0, 0x00000800), + expected: BigInt.one << 43, + ), + ( + name: '2^44', + value: Int64.fromWords(0, 0x00001000), + expected: BigInt.one << 44, + ), + ( + name: '2^45', + value: Int64.fromWords(0, 0x00002000), + expected: BigInt.one << 45, + ), + ( + name: '2^62', + value: Int64.fromWords(0, 0x40000000), + expected: BigInt.one << 62, + ), + ( + name: 'signed min', + value: Int64.fromWords(0, 0x80000000), + expected: -(BigInt.one << 63), + ), + ( + name: 'signed max', + value: Int64.fromWords(0xffffffff, 0x7fffffff), + expected: (BigInt.one << 63) - BigInt.one, + ), + ( + name: 'negative one', + value: Int64.fromWords(0xffffffff, 0xffffffff), + expected: -BigInt.one, + ), + ( + name: 'parseHex signed min', + value: Int64.parseHex('8000000000000000'), + expected: -(BigInt.one << 63), + ), + ( + name: 'parseHex negative', + value: Int64.parseHex('-7'), + expected: BigInt.from(-7), + ), + ( + name: 'parseHex wraparound', + value: Int64.parseHex('ffffffffffffffff'), + expected: -BigInt.one, + ), + ]; + + for (final testCase in cases) { + _expectInt64State( + testCase.value, + testCase.expected, + reason: testCase.name, + ); + } + }); + + test('equality and compare respect signed ordering', () { + final values = [ + Int64.parseHex('8000000000000000'), + Int64.parseHex('ffffffffffffffff'), + Int64(0), + Int64(1), + Int64.parseHex('7fffffffffffffff'), + ]; + + expect(values[0] == values[0], isTrue); + expect(values[0].compareTo(values[1]), lessThan(0)); + expect(values[1].compareTo(values[2]), lessThan(0)); + expect(values[2].compareTo(values[3]), lessThan(0)); + expect(values[3].compareTo(values[4]), lessThan(0)); + expect(values[4].compareTo(values[4]), equals(0)); + expect(values[1], equals(Int64.fromWords(0xffffffff, 0xffffffff))); + }); + + test('arithmetic wraps, borrows, and truncates with sign edge cases', () { + _expectInt64State( + Int64.parseHex('7fffffffffffffff') + 1, + -(BigInt.one << 63), + reason: 'signed overflow add', + ); + _expectInt64State( + Int64.parseHex('8000000000000000') - 1, + (BigInt.one << 63) - BigInt.one, + reason: 'signed underflow subtract', + ); + _expectInt64State( + Int64(-1) + Int64(1), + BigInt.zero, + reason: 'wrapper addition', + ); + _expectInt64State( + Int64(0) - 1, + -BigInt.one, + reason: 'borrow from zero', + ); + _expectInt64State( + Int64.parseHex('4000000000000000') * 2, + -(BigInt.one << 63), + reason: 'multiply overflow', + ); + _expectInt64State( + Int64.parseHex('8000000000000000') * -1, + -(BigInt.one << 63), + reason: 'multiply signed min by -1 wraps', + ); + _expectInt64State( + Int64.parseHex('-7') ~/ 3, + BigInt.from(-2), + reason: 'truncating division toward zero', + ); + _expectInt64State( + Int64.parseHex('-7') % 3, + BigInt.from(2), + reason: 'remainder follows Dart int semantics', + ); + expect(Int64.parseHex('-7') / 2, equals(-3.5)); + expect((-Int64(1)).toBigInt(), equals(BigInt.from(-1))); + expect((-Int64.parseHex('8000000000000000')).toBigInt(), + equals(-(BigInt.one << 63))); + }); + + test('bitwise operators and shifts cross segment boundaries', () { + final left = Int64.parseHex('f23456789abcdef0'); + final right = Int64.parseHex('00ff00ff00ff00ff'); + final leftBig = left.toBigInt(); + final rightBig = right.toBigInt(); + + _expectInt64State(left & right, _normalizeSigned64(leftBig & rightBig)); + _expectInt64State(left | right, _normalizeSigned64(leftBig | rightBig)); + _expectInt64State(left ^ right, _normalizeSigned64(leftBig ^ rightBig)); + _expectInt64State(~left, _normalizeSigned64(~leftBig)); + _expectInt64State(Int64(0x7f) & 0x3, BigInt.from(0x3)); + _expectInt64State(Int64(0x7f) | Int64(0x80), BigInt.from(0xff)); + _expectInt64State(Int64(0x7f) ^ 0x3, BigInt.from(0x7c)); + + const shifts = [ + 0, + 1, + 21, + 22, + 23, + 31, + 32, + 43, + 44, + 45, + 62, + 63, + 64, + 65 + ]; + for (final shift in shifts) { + _expectInt64State( + left << shift, + _normalizeSigned64(leftBig << shift), + reason: 'left shift $shift', + ); + _expectInt64State( + left >> shift, + _normalizeSigned64(leftBig >> shift), + reason: 'right shift $shift', + ); + _expectInt64State( + left >>> shift, + _normalizeSigned64(_normalizeUnsigned64(leftBig) >> shift), + reason: 'logical shift $shift', + ); + } + + expect(() => left << -1, throwsArgumentError); + expect(() => left >> -1, throwsArgumentError); + expect(() => left >>> -1, throwsArgumentError); + }); + + test('toInt respects JS-safe and platform-specific unsafe boundaries', () { + _expectInt64State( + Int64.parseHex('1fffffffffffff'), + _jsSafeMax, + reason: 'safe positive max', + ); + _expectInt64State( + Int64.parseHex('-1fffffffffffff'), + _jsSafeMin, + reason: 'safe negative min', + ); + _expectInt64State( + Int64.parseHex('20000000000000'), + BigInt.one << 53, + reason: 'unsafe positive boundary', + ); + _expectInt64State( + Int64.parseHex('-20000000000000'), + -(BigInt.one << 53), + reason: 'unsafe negative boundary', + ); + }); + }); + + group('Uint64', () { + test('constructors normalize words and hex edge cases', () { + final cases = <({String name, Uint64 value, BigInt expected})>[ + (name: 'zero', value: Uint64.fromWords(0, 0), expected: BigInt.zero), + (name: 'one', value: Uint64.fromWords(1, 0), expected: BigInt.one), + ( + name: '2^22', + value: Uint64.fromWords(0x00400000, 0), + expected: BigInt.one << 22, + ), + ( + name: '2^23', + value: Uint64.fromWords(0x00800000, 0), + expected: BigInt.one << 23, + ), + ( + name: '2^31', + value: Uint64.fromWords(0x80000000, 0), + expected: BigInt.one << 31, + ), + ( + name: '2^32', + value: Uint64.fromWords(0, 1), + expected: BigInt.one << 32, + ), + ( + name: '2^43', + value: Uint64.fromWords(0, 0x00000800), + expected: BigInt.one << 43, + ), + ( + name: '2^44', + value: Uint64.fromWords(0, 0x00001000), + expected: BigInt.one << 44, + ), + ( + name: '2^45', + value: Uint64.fromWords(0, 0x00002000), + expected: BigInt.one << 45, + ), + ( + name: '2^62', + value: Uint64.fromWords(0, 0x40000000), + expected: BigInt.one << 62, + ), + ( + name: '2^63', + value: Uint64.fromWords(0, 0x80000000), + expected: BigInt.one << 63, + ), + ( + name: 'max', + value: Uint64.fromWords(0xffffffff, 0xffffffff), + expected: _mask64, + ), + ( + name: 'parseHex max', + value: Uint64.parseHex('ffffffffffffffff'), + expected: _mask64, + ), + ( + name: 'parseHex negative one wraps', + value: Uint64.parseHex('-1'), + expected: _mask64, + ), + ]; + + for (final testCase in cases) { + _expectUint64State( + testCase.value, + testCase.expected, + reason: testCase.name, + ); + } + }); + + test('equality and compare respect unsigned ordering', () { + final values = [ + Uint64(0), + Uint64(1), + Uint64.parseHex('7fffffffffffffff'), + Uint64.parseHex('8000000000000000'), + Uint64.parseHex('ffffffffffffffff'), + ]; + + expect(values[0] == values[0], isTrue); + expect(values[0].compareTo(values[1]), lessThan(0)); + expect(values[1].compareTo(values[2]), lessThan(0)); + expect(values[2].compareTo(values[3]), lessThan(0)); + expect(values[3].compareTo(values[4]), lessThan(0)); + expect(values[4].compareTo(values[4]), equals(0)); + expect(values[4], equals(Uint64.fromWords(0xffffffff, 0xffffffff))); + }); + + test('arithmetic wraps, borrows, and truncates across the full range', () { + _expectUint64State( + Uint64.parseHex('ffffffffffffffff') + 1, + BigInt.zero, + reason: 'overflow add', + ); + _expectUint64State( + Uint64(0) - 1, + _mask64, + reason: 'borrow from zero', + ); + _expectUint64State( + Uint64.parseHex('8000000000000000') * 2, + BigInt.zero, + reason: 'multiply overflow', + ); + _expectUint64State( + Uint64.parseHex('ffffffffffffffff') * 2, + _mask64 - BigInt.one, + reason: 'multiply max by two', + ); + _expectUint64State( + Uint64.parseHex('ffffffffffffffff') ~/ 3, + _mask64 ~/ BigInt.from(3), + reason: 'truncating division', + ); + _expectUint64State( + Uint64.parseHex('ffffffffffffffff') % 3, + BigInt.from(0), + reason: 'remainder against three', + ); + expect(Uint64.parseHex('7') / 2, equals(3.5)); + expect((-Uint64(1)).toBigInt(), equals(_mask64)); + expect((-Uint64.parseHex('8000000000000000')).toBigInt(), + equals(BigInt.one << 63)); + }); + + test('bitwise operators and shifts cross segment boundaries', () { + final left = Uint64.parseHex('f23456789abcdef0'); + final right = Uint64.parseHex('00ff00ff00ff00ff'); + final leftBig = left.toBigInt(); + final rightBig = right.toBigInt(); + + _expectUint64State( + left & right, _normalizeUnsigned64(leftBig & rightBig)); + _expectUint64State( + left | right, _normalizeUnsigned64(leftBig | rightBig)); + _expectUint64State( + left ^ right, _normalizeUnsigned64(leftBig ^ rightBig)); + _expectUint64State(~left, _normalizeUnsigned64(~leftBig)); + _expectUint64State(Uint64(0x7f) & 0x3, BigInt.from(0x3)); + _expectUint64State(Uint64(0x7f) | Uint64(0x80), BigInt.from(0xff)); + _expectUint64State(Uint64(0x7f) ^ 0x3, BigInt.from(0x7c)); + + const shifts = [ + 0, + 1, + 21, + 22, + 23, + 31, + 32, + 43, + 44, + 45, + 62, + 63, + 64, + 65 + ]; + for (final shift in shifts) { + _expectUint64State( + left << shift, + _normalizeUnsigned64(leftBig << shift), + reason: 'left shift $shift', + ); + _expectUint64State( + left >> shift, + _normalizeUnsigned64(leftBig >> shift), + reason: 'right shift $shift', + ); + _expectUint64State( + left >>> shift, + _normalizeUnsigned64(leftBig >> shift), + reason: 'logical shift $shift', + ); + } + + expect(() => left << -1, throwsArgumentError); + expect(() => left >> -1, throwsArgumentError); + expect(() => left >>> -1, throwsArgumentError); + }); + + test('toInt respects JS-safe and native unsigned boundaries', () { + _expectUint64State( + Uint64.parseHex('1fffffffffffff'), + _jsSafeMax, + reason: 'safe positive max', + ); + _expectUint64State( + Uint64.parseHex('20000000000000'), + BigInt.one << 53, + reason: 'unsafe positive boundary', + ); + _expectUint64State( + Uint64.parseHex('8000000000000000'), + BigInt.one << 63, + reason: 'native-unsafe high-bit boundary', + ); + _expectUint64State( + Uint64.parseHex('ffffffffffffffff'), + _mask64, + reason: 'max boundary', + ); + }); + }); +} From 4717017d27becb06ac4076cca59390e81c04f4cb Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 15:53:07 +0800 Subject: [PATCH 07/14] refactor dart int64 write/read --- dart/packages/fory/lib/fory.dart | 2 +- .../fory/lib/src/codegen/fory_generator.dart | 15 +- .../generated_cursor.dart} | 4 +- .../src/codegen/generated_cursor_mixin.dart | 227 ++++++++ .../src/codegen/generated_cursor_native.dart | 297 +++++++++++ .../lib/src/codegen/generated_cursor_web.dart | 323 ++++++++++++ .../lib/src/codegen/generated_support.dart | 484 +----------------- .../lib/src/context/meta_string_reader.dart | 2 +- .../lib/src/context/meta_string_writer.dart | 2 +- .../fory/lib/src/context/read_context.dart | 2 +- .../fory/lib/src/context/ref_reader.dart | 2 +- .../fory/lib/src/context/ref_writer.dart | 2 +- .../fory/lib/src/context/write_context.dart | 4 +- dart/packages/fory/lib/src/fory.dart | 2 +- .../buffer.dart} | 2 +- .../{buffer.dart => memory/buffer_mixin.dart} | 154 +----- .../fory/lib/src/memory/buffer_native.dart | 267 ++++++++++ .../fory/lib/src/memory/buffer_web.dart | 289 +++++++++++ .../packages/fory/lib/src/meta/type_meta.dart | 2 +- .../fory/lib/src/resolver/type_resolver.dart | 2 +- .../lib/src/serializer/map_serializers.dart | 2 +- .../src/serializer/scalar_serializers.dart | 19 +- .../fory/lib/src/util/int64_codec.dart | 92 ---- .../lib/src/util/int64_platform_native.dart | 20 - .../fory/lib/src/util/string_util.dart | 2 +- .../fory/test/string_encoding_test.dart | 2 +- docs/guide/dart/troubleshooting.md | 2 +- docs/guide/dart/web-platform-support.md | 20 + 28 files changed, 1491 insertions(+), 752 deletions(-) rename dart/packages/fory/lib/src/{util/int64_platform.dart => codegen/generated_cursor.dart} (88%) create mode 100644 dart/packages/fory/lib/src/codegen/generated_cursor_mixin.dart create mode 100644 dart/packages/fory/lib/src/codegen/generated_cursor_native.dart create mode 100644 dart/packages/fory/lib/src/codegen/generated_cursor_web.dart rename dart/packages/fory/lib/src/{util/int64_platform_web.dart => memory/buffer.dart} (91%) rename dart/packages/fory/lib/src/{buffer.dart => memory/buffer_mixin.dart} (70%) create mode 100644 dart/packages/fory/lib/src/memory/buffer_native.dart create mode 100644 dart/packages/fory/lib/src/memory/buffer_web.dart delete mode 100644 dart/packages/fory/lib/src/util/int64_codec.dart delete mode 100644 dart/packages/fory/lib/src/util/int64_platform_native.dart create mode 100644 docs/guide/dart/web-platform-support.md diff --git a/dart/packages/fory/lib/fory.dart b/dart/packages/fory/lib/fory.dart index 67fac76e45..5bc276e9be 100644 --- a/dart/packages/fory/lib/fory.dart +++ b/dart/packages/fory/lib/fory.dart @@ -30,7 +30,7 @@ export 'src/annotation/fory_struct.dart'; export 'src/annotation/fory_union.dart'; export 'src/annotation/type_spec.dart'; export 'src/annotation/numeric_types.dart'; -export 'src/buffer.dart' +export 'src/memory/buffer.dart' hide bufferByteData, bufferBytes, diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index c4189aaf7f..a2860e550c 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -1234,10 +1234,19 @@ GeneratedFieldType( case TypeIds.varInt32: return 'buffer.writeVarInt32(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.int64: + if (field.type.isDartCoreInt) { + return 'buffer.writeInt64FromInt($valueExpression)'; + } return 'buffer.writeInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.varInt64: + if (field.type.isDartCoreInt) { + return 'buffer.writeVarInt64FromInt($valueExpression)'; + } return 'buffer.writeVarInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.taggedInt64: + if (field.type.isDartCoreInt) { + return 'buffer.writeTaggedInt64FromInt($valueExpression)'; + } return 'buffer.writeTaggedInt64(${_directGeneratedScalarExpression(field, valueExpression)})'; case TypeIds.uint8: return 'buffer.writeUint8(${_directGeneratedScalarExpression(field, valueExpression)})'; @@ -1400,15 +1409,15 @@ GeneratedFieldType( : 'Int32(buffer.readVarInt32())'; case TypeIds.int64: return field.type.isDartCoreInt - ? 'buffer.readInt64().toInt()' + ? 'buffer.readInt64AsInt()' : 'buffer.readInt64()'; case TypeIds.varInt64: return field.type.isDartCoreInt - ? 'buffer.readVarInt64().toInt()' + ? 'buffer.readVarInt64AsInt()' : 'buffer.readVarInt64()'; case TypeIds.taggedInt64: return field.type.isDartCoreInt - ? 'buffer.readTaggedInt64().toInt()' + ? 'buffer.readTaggedInt64AsInt()' : 'buffer.readTaggedInt64()'; case TypeIds.uint8: return field.type.isDartCoreInt diff --git a/dart/packages/fory/lib/src/util/int64_platform.dart b/dart/packages/fory/lib/src/codegen/generated_cursor.dart similarity index 88% rename from dart/packages/fory/lib/src/util/int64_platform.dart rename to dart/packages/fory/lib/src/codegen/generated_cursor.dart index 7b9d0473ea..44816f1189 100644 --- a/dart/packages/fory/lib/src/util/int64_platform.dart +++ b/dart/packages/fory/lib/src/codegen/generated_cursor.dart @@ -17,5 +17,5 @@ * under the License. */ -export 'int64_platform_native.dart' - if (dart.library.js_interop) 'int64_platform_web.dart'; +export 'generated_cursor_native.dart' + if (dart.library.js_interop) 'generated_cursor_web.dart'; diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_mixin.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_mixin.dart new file mode 100644 index 0000000000..d8aa225336 --- /dev/null +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_mixin.dart @@ -0,0 +1,227 @@ +/* + * 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. + */ + +// ignore_for_file: use_string_in_part_of_directives + +part of fory.src.codegen.generated_cursor; + +mixin _GeneratedWriteCursorMixin { + late final Buffer _buffer; + late final Uint8List _bytes; + late final ByteData _view; + int _offset = 0; + + void _initWriteCursor(Buffer buffer, int maxBytes) { + final start = bufferReserveBytes(buffer, maxBytes); + _buffer = buffer; + _bytes = bufferBytes(buffer); + _view = bufferByteData(buffer); + _offset = start; + } + + void finish() { + bufferSetWriterIndex(_buffer, _offset); + } + + @pragma('vm:prefer-inline') + void writeBool(bool value) { + _bytes[_offset] = value ? 1 : 0; + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeByte(int value) { + _view.setInt8(_offset, value); + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeUint8(int value) { + _view.setUint8(_offset, value); + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeInt16(int value) { + _view.setInt16(_offset, value, Endian.little); + _offset += 2; + } + + @pragma('vm:prefer-inline') + void writeUint16(int value) { + _view.setUint16(_offset, value, Endian.little); + _offset += 2; + } + + @pragma('vm:prefer-inline') + void writeInt32(int value) { + _view.setInt32(_offset, value, Endian.little); + _offset += 4; + } + + @pragma('vm:prefer-inline') + void writeUint32(int value) { + _view.setUint32(_offset, value, Endian.little); + _offset += 4; + } + + @pragma('vm:prefer-inline') + void writeFloat16(Float16 value) { + writeUint16(value.toBits()); + } + + @pragma('vm:prefer-inline') + void writeBfloat16(Bfloat16 value) { + writeUint16(value.toBits()); + } + + @pragma('vm:prefer-inline') + void writeFloat32(double value) { + _view.setFloat32(_offset, value, Endian.little); + _offset += 4; + } + + @pragma('vm:prefer-inline') + void writeFloat64(double value) { + _view.setFloat64(_offset, value, Endian.little); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeVarUint32(int value) { + var remaining = value; + while (remaining >= 0x80) { + _bytes[_offset] = (remaining & 0x7f) | 0x80; + _offset += 1; + remaining >>>= 7; + } + _bytes[_offset] = remaining; + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeVarInt32(int value) { + writeVarUint32(((value << 1) ^ (value >> 31)).toUnsigned(32)); + } + + void writeVarUint64(Uint64 value); +} + +mixin _GeneratedReadCursorMixin { + late final Buffer _buffer; + late final ByteData _view; + int _offset = 0; + + void _initReadCursor(Buffer buffer) { + _buffer = buffer; + _view = bufferByteData(buffer); + _offset = bufferReaderIndex(buffer); + } + + void finish() { + bufferSetReaderIndex(_buffer, _offset); + } + + @pragma('vm:prefer-inline') + bool readBool() => readUint8() != 0; + + @pragma('vm:prefer-inline') + int readByte() { + final value = _view.getInt8(_offset); + _offset += 1; + return value; + } + + @pragma('vm:prefer-inline') + int readUint8() { + final value = _view.getUint8(_offset); + _offset += 1; + return value; + } + + @pragma('vm:prefer-inline') + int readInt16() { + final value = _view.getInt16(_offset, Endian.little); + _offset += 2; + return value; + } + + @pragma('vm:prefer-inline') + int readUint16() { + final value = _view.getUint16(_offset, Endian.little); + _offset += 2; + return value; + } + + @pragma('vm:prefer-inline') + int readInt32() { + final value = _view.getInt32(_offset, Endian.little); + _offset += 4; + return value; + } + + @pragma('vm:prefer-inline') + int readUint32() { + final value = _view.getUint32(_offset, Endian.little); + _offset += 4; + return value; + } + + @pragma('vm:prefer-inline') + Float16 readFloat16() => Float16.fromBits(readUint16()); + + @pragma('vm:prefer-inline') + Bfloat16 readBfloat16() => Bfloat16.fromBits(readUint16()); + + @pragma('vm:prefer-inline') + double readFloat32() { + final value = _view.getFloat32(_offset, Endian.little); + _offset += 4; + return value; + } + + @pragma('vm:prefer-inline') + double readFloat64() { + final value = _view.getFloat64(_offset, Endian.little); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + int readVarUint32() { + var shift = 0; + var result = 0; + while (true) { + final byte = readUint8(); + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + } + + @pragma('vm:prefer-inline') + int readVarInt32() { + final value = readVarUint32(); + return ((value >>> 1) ^ -(value & 1)).toSigned(32); + } + + Uint64 readVarUint64(); +} diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart new file mode 100644 index 0000000000..5c9e987a36 --- /dev/null +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart @@ -0,0 +1,297 @@ +/* + * 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. + */ + +// ignore_for_file: unnecessary_library_name + +library fory.src.codegen.generated_cursor; + +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +import 'package:fory/src/memory/buffer.dart'; +import 'package:fory/src/types/bfloat16.dart'; +import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; + +part 'generated_cursor_mixin.dart'; + +@internal +final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { + GeneratedWriteCursor._(); + + factory GeneratedWriteCursor.reserve(Buffer buffer, int maxBytes) { + return GeneratedWriteCursor._().._initWriteCursor(buffer, maxBytes); + } + + @pragma('vm:prefer-inline') + void writeInt64(Int64 value) { + _view.setInt64(_offset, value.toInt(), Endian.little); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeInt64FromInt(int value) { + _view.setInt64(_offset, value, Endian.little); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeUint64(Uint64 value) { + _view.setUint32(_offset, value.low32, Endian.little); + _view.setUint32(_offset + 4, value.high32Unsigned, Endian.little); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeUint64FromInt(int value) { + _view.setUint32(_offset, value & 0xffffffff, Endian.little); + _view.setUint32(_offset + 4, (value >> 32) & 0xffffffff, Endian.little); + _offset += 8; + } + + @pragma('vm:prefer-inline') + @override + void writeVarUint64(Uint64 value) { + var remaining = value; + for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { + _bytes[_offset] = (remaining.low32 & 0x7f) | 0x80; + _offset += 1; + remaining = remaining >> 7; + } + _bytes[_offset] = remaining.toInt(); + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeVarUint64FromInt(int value) { + if (value < 0) { + writeVarUint64(Uint64(value)); + return; + } + _writeVarUint64Int(value); + } + + @pragma('vm:prefer-inline') + void writeVarInt64(Int64 value) { + writeVarUint64(Uint64((value << 1) ^ (value >> 63))); + } + + @pragma('vm:prefer-inline') + void writeVarInt64FromInt(int value) { + _writeVarUint64Int((value << 1) ^ (value >> 63)); + } + + @pragma('vm:prefer-inline') + void writeTaggedInt64(Int64 value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value.toInt() << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedInt64FromInt(int value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64FromInt(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedUint64(Uint64 value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value.toInt() << 1); + return; + } + writeUint8(0x01); + writeUint64(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedUint64FromInt(int value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value << 1); + return; + } + writeUint8(0x01); + writeUint64FromInt(value); + } + + @pragma('vm:prefer-inline') + void _writeVarUint64Int(int value) { + var remaining = value; + for (var index = 0; index < 8; index += 1) { + final chunk = remaining & 0x7f; + remaining >>>= 7; + if (remaining == 0) { + _bytes[_offset] = chunk; + _offset += 1; + return; + } + _bytes[_offset] = chunk | 0x80; + _offset += 1; + } + _bytes[_offset] = remaining & 0xff; + _offset += 1; + } +} + +@internal +final class GeneratedReadCursor with _GeneratedReadCursorMixin { + GeneratedReadCursor._(); + + factory GeneratedReadCursor.start(Buffer buffer) { + return GeneratedReadCursor._().._initReadCursor(buffer); + } + + @pragma('vm:prefer-inline') + Int64 readInt64() { + final value = Int64(_view.getInt64(_offset, Endian.little)); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + int readInt64AsInt() { + final value = _view.getInt64(_offset, Endian.little); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + Uint64 readUint64() { + final value = Uint64.fromWords( + _view.getUint32(_offset, Endian.little), + _view.getUint32(_offset + 4, Endian.little), + ); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + int readUint64AsInt() { + final value = _view.getUint64(_offset, Endian.little); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + @override + Uint64 readVarUint64() { + var shift = 0; + var result = Uint64(0); + while (shift < 56) { + final byte = readUint8(); + result = result | (Uint64(byte & 0x7f) << shift); + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + return result | (Uint64(readUint8()) << 56); + } + + @pragma('vm:prefer-inline') + int readVarUint64AsInt() { + var shift = 0; + var result = 0; + while (shift < 56) { + final byte = readUint8(); + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + return result | (readUint8() << 56); + } + + @pragma('vm:prefer-inline') + Int64 readVarInt64() { + final encoded = readVarUint64(); + return Int64((encoded >>> 1) ^ -(encoded & 1)); + } + + @pragma('vm:prefer-inline') + int readVarInt64AsInt() { + final encoded = readVarUint64AsInt(); + return (encoded >>> 1) ^ -(encoded & 1); + } + + @pragma('vm:prefer-inline') + Int64 readTaggedInt64() { + final readIndex = _offset; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return Int64(first.toSigned(32) ~/ 2); + } + final value = Int64(_view.getInt64(readIndex + 1, Endian.little)); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + int readTaggedInt64AsInt() { + final readIndex = _offset; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >> 1; + } + final value = _view.getInt64(readIndex + 1, Endian.little); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + Uint64 readTaggedUint64() { + final readIndex = _offset; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return Uint64(first >>> 1); + } + final value = Uint64.fromWords( + _view.getUint32(readIndex + 1, Endian.little), + _view.getUint32(readIndex + 5, Endian.little), + ); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + int readTaggedUint64AsInt() { + final readIndex = _offset; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >>> 1; + } + final value = _view.getUint64(readIndex + 1, Endian.little); + _offset = readIndex + 9; + return value; + } +} diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart new file mode 100644 index 0000000000..cd7dabb302 --- /dev/null +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart @@ -0,0 +1,323 @@ +/* + * 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. + */ + +// ignore_for_file: unnecessary_library_name + +library fory.src.codegen.generated_cursor; + +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +import 'package:fory/src/memory/buffer.dart'; +import 'package:fory/src/types/bfloat16.dart'; +import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; + +part 'generated_cursor_mixin.dart'; + +const int _jsSafeIntMax = 9007199254740991; +const int _jsSafeIntMin = -9007199254740991; + +@internal +final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { + GeneratedWriteCursor._(); + + factory GeneratedWriteCursor.reserve(Buffer buffer, int maxBytes) { + return GeneratedWriteCursor._().._initWriteCursor(buffer, maxBytes); + } + + @pragma('vm:prefer-inline') + void writeInt64(Int64 value) { + _writeInt64Words(_offset, value); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeInt64FromInt(int value) { + _writeInt64Words(_offset, _int64FromInt(value)); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeUint64(Uint64 value) { + _writeUint64Words(_offset, value); + _offset += 8; + } + + @pragma('vm:prefer-inline') + void writeUint64FromInt(int value) { + _writeUint64Words(_offset, Uint64(value)); + _offset += 8; + } + + @pragma('vm:prefer-inline') + @override + void writeVarUint64(Uint64 value) { + var remaining = value; + for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { + _bytes[_offset] = (remaining.low32 & 0x7f) | 0x80; + _offset += 1; + remaining = remaining >> 7; + } + _bytes[_offset] = remaining.toInt(); + _offset += 1; + } + + @pragma('vm:prefer-inline') + void writeVarUint64FromInt(int value) { + writeVarUint64(Uint64(value)); + } + + @pragma('vm:prefer-inline') + void writeVarInt64(Int64 value) { + writeVarUint64(_zigZagEncodeInt64(value)); + } + + @pragma('vm:prefer-inline') + void writeVarInt64FromInt(int value) { + writeVarInt64(_int64FromInt(value)); + } + + @pragma('vm:prefer-inline') + void writeTaggedInt64(Int64 value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value.toInt() << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedInt64FromInt(int value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value << 1).toSigned(32)); + return; + } + _checkInt64IntRange(value); + writeUint8(0x01); + writeInt64FromInt(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedUint64(Uint64 value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value.toInt() << 1); + return; + } + writeUint8(0x01); + writeUint64(value); + } + + @pragma('vm:prefer-inline') + void writeTaggedUint64FromInt(int value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value << 1); + return; + } + writeUint8(0x01); + writeUint64FromInt(value); + } + + @pragma('vm:prefer-inline') + void _writeInt64Words(int offset, Int64 value) { + _view.setUint32(offset, value.low32, Endian.little); + _view.setUint32(offset + 4, value.high32Unsigned, Endian.little); + } + + @pragma('vm:prefer-inline') + void _writeUint64Words(int offset, Uint64 value) { + _view.setUint32(offset, value.low32, Endian.little); + _view.setUint32(offset + 4, value.high32Unsigned, Endian.little); + } +} + +@internal +final class GeneratedReadCursor with _GeneratedReadCursorMixin { + GeneratedReadCursor._(); + + factory GeneratedReadCursor.start(Buffer buffer) { + return GeneratedReadCursor._().._initReadCursor(buffer); + } + + @pragma('vm:prefer-inline') + Int64 readInt64() { + final value = _readInt64Words(_offset); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + int readInt64AsInt() { + final value = _int64ToInt(_readInt64Words(_offset)); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + Uint64 readUint64() { + final value = _readUint64Words(_offset); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + int readUint64AsInt() { + final value = _readUint64Words(_offset).toInt(); + _offset += 8; + return value; + } + + @pragma('vm:prefer-inline') + @override + Uint64 readVarUint64() { + var shift = 0; + var result = Uint64(0); + while (shift < 56) { + final byte = readUint8(); + result = result | (Uint64(byte & 0x7f) << shift); + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + return result | (Uint64(readUint8()) << 56); + } + + @pragma('vm:prefer-inline') + int readVarUint64AsInt() { + return readVarUint64().toInt(); + } + + @pragma('vm:prefer-inline') + Int64 readVarInt64() { + return _zigZagDecodeInt64(readVarUint64()); + } + + @pragma('vm:prefer-inline') + int readVarInt64AsInt() { + return _int64ToInt(readVarInt64()); + } + + @pragma('vm:prefer-inline') + Int64 readTaggedInt64() { + final readIndex = _offset; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return Int64(first.toSigned(32) ~/ 2); + } + final value = _readInt64Words(readIndex + 1); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + int readTaggedInt64AsInt() { + final readIndex = _offset; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >> 1; + } + final value = _int64ToInt(_readInt64Words(readIndex + 1)); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + Uint64 readTaggedUint64() { + final readIndex = _offset; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return Uint64(first >>> 1); + } + final value = _readUint64Words(readIndex + 1); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + int readTaggedUint64AsInt() { + final readIndex = _offset; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _offset = readIndex + 4; + return first >>> 1; + } + final value = _readUint64Words(readIndex + 1).toInt(); + _offset = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + Int64 _readInt64Words(int offset) { + return Int64.fromWords( + _view.getUint32(offset, Endian.little), + _view.getInt32(offset + 4, Endian.little), + ); + } + + @pragma('vm:prefer-inline') + Uint64 _readUint64Words(int offset) { + return Uint64.fromWords( + _view.getUint32(offset, Endian.little), + _view.getUint32(offset + 4, Endian.little), + ); + } +} + +@pragma('vm:prefer-inline') +void _checkInt64IntRange(int value) { + if (value < _jsSafeIntMin || value > _jsSafeIntMax) { + throw StateError( + 'Dart int value $value is outside the JS-safe signed int64 range ' + '[$_jsSafeIntMin, $_jsSafeIntMax]. Use Int64 for full 64-bit values ' + 'on web.', + ); + } +} + +@pragma('vm:prefer-inline') +Int64 _int64FromInt(int value) { + _checkInt64IntRange(value); + return Int64(value); +} + +@pragma('vm:prefer-inline') +int _int64ToInt(Int64 value) => value.toInt(); + +@pragma('vm:prefer-inline') +Uint64 _zigZagEncodeInt64(Int64 value) { + final encoded = (value << 1) ^ (value >> 63); + return Uint64.fromWords(encoded.low32, encoded.high32Unsigned); +} + +@pragma('vm:prefer-inline') +Int64 _zigZagDecodeInt64(Uint64 encoded) { + final magnitude = encoded >> 1; + final decoded = Int64.fromWords(magnitude.low32, magnitude.high32Unsigned); + if ((encoded.low32 & 1) == 0) { + return decoded; + } + return -(decoded + 1); +} diff --git a/dart/packages/fory/lib/src/codegen/generated_support.dart b/dart/packages/fory/lib/src/codegen/generated_support.dart index 023bd5f3b2..645a0b86f2 100644 --- a/dart/packages/fory/lib/src/codegen/generated_support.dart +++ b/dart/packages/fory/lib/src/codegen/generated_support.dart @@ -22,7 +22,8 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:fory/fory.dart'; -import 'package:fory/src/buffer.dart'; +export 'package:fory/src/codegen/generated_cursor.dart'; + import 'package:fory/src/codegen/generated_registry.dart'; import 'package:fory/src/meta/field_info.dart' as meta; import 'package:fory/src/meta/field_type.dart' as meta_types; @@ -36,487 +37,6 @@ import 'package:fory/src/serializer/struct_serializer.dart'; import 'package:fory/src/serializer/struct_slots.dart'; import 'package:fory/src/serializer/time_serializers.dart'; import 'package:fory/src/serializer/typed_array_serializers.dart'; -import 'package:fory/src/util/int64_codec.dart'; -import 'package:fory/src/util/int64_platform.dart'; - -@internal -final class GeneratedWriteCursor { - final Buffer _buffer; - final Uint8List _bytes; - final ByteData _view; - int _offset; - - GeneratedWriteCursor._( - this._buffer, - this._bytes, - this._view, - this._offset, - ); - - factory GeneratedWriteCursor.reserve(Buffer buffer, int maxBytes) { - final start = bufferReserveBytes(buffer, maxBytes); - return GeneratedWriteCursor._( - buffer, - bufferBytes(buffer), - bufferByteData(buffer), - start, - ); - } - - void finish() { - bufferSetWriterIndex(_buffer, _offset); - } - - @pragma('vm:prefer-inline') - void writeBool(bool value) { - _bytes[_offset] = value ? 1 : 0; - _offset += 1; - } - - @pragma('vm:prefer-inline') - void writeByte(int value) { - _view.setInt8(_offset, value); - _offset += 1; - } - - @pragma('vm:prefer-inline') - void writeUint8(int value) { - _view.setUint8(_offset, value); - _offset += 1; - } - - @pragma('vm:prefer-inline') - void writeInt16(int value) { - _view.setInt16(_offset, value, Endian.little); - _offset += 2; - } - - @pragma('vm:prefer-inline') - void writeUint16(int value) { - _view.setUint16(_offset, value, Endian.little); - _offset += 2; - } - - @pragma('vm:prefer-inline') - void writeInt32(int value) { - _view.setInt32(_offset, value, Endian.little); - _offset += 4; - } - - @pragma('vm:prefer-inline') - void writeUint32(int value) { - _view.setUint32(_offset, value, Endian.little); - _offset += 4; - } - - @pragma('vm:prefer-inline') - void writeInt64(Int64 value) { - writeInt64LittleEndian(_view, _offset, value); - _offset += 8; - } - - @pragma('vm:prefer-inline') - void writeInt64FromInt(int value) { - if (useNativeInt64FastPath) { - _view.setInt64(_offset, value, Endian.little); - _offset += 8; - return; - } - writeInt64(Int64(value)); - } - - @pragma('vm:prefer-inline') - void writeUint64(Uint64 value) { - writeUint64LittleEndian(_view, _offset, value); - _offset += 8; - } - - @pragma('vm:prefer-inline') - void writeUint64FromInt(int value) { - if (useNativeInt64FastPath) { - _view.setUint64(_offset, value, Endian.little); - _offset += 8; - return; - } - writeUint64(Uint64(value)); - } - - @pragma('vm:prefer-inline') - void writeFloat16(Float16 value) { - writeUint16(value.toBits()); - } - - @pragma('vm:prefer-inline') - void writeBfloat16(Bfloat16 value) { - writeUint16(value.toBits()); - } - - @pragma('vm:prefer-inline') - void writeFloat32(double value) { - _view.setFloat32(_offset, value, Endian.little); - _offset += 4; - } - - @pragma('vm:prefer-inline') - void writeFloat64(double value) { - _view.setFloat64(_offset, value, Endian.little); - _offset += 8; - } - - @pragma('vm:prefer-inline') - void writeVarUint32(int value) { - var remaining = value; - while (remaining >= 0x80) { - _bytes[_offset] = (remaining & 0x7f) | 0x80; - _offset += 1; - remaining >>>= 7; - } - _bytes[_offset] = remaining; - _offset += 1; - } - - @pragma('vm:prefer-inline') - void writeVarInt32(int value) { - final encoded = (value << 1) ^ (value >> 31); - writeVarUint32( - useNativeInt64FastPath ? encoded : encoded.toUnsigned(32), - ); - } - - @pragma('vm:prefer-inline') - void writeVarUint64(Uint64 value) { - writeVarUint64Bytes(value, (byte) { - _bytes[_offset] = byte; - _offset += 1; - }); - } - - @pragma('vm:prefer-inline') - void writeVarUint64FromInt(int value) { - if (useNativeInt64FastPath) { - _writeVarUint64Int(value); - return; - } - writeVarUint64(Uint64(value)); - } - - @pragma('vm:prefer-inline') - void writeVarInt64(Int64 value) { - writeVarUint64(zigZagEncodeInt64(value)); - } - - @pragma('vm:prefer-inline') - void writeVarInt64FromInt(int value) { - if (useNativeInt64FastPath) { - _writeVarUint64Int((value << 1) ^ (value >> 63)); - return; - } - writeVarInt64(Int64(value)); - } - - @pragma('vm:prefer-inline') - void writeTaggedInt64(Int64 value) { - if (value >= -0x40000000 && value <= 0x3fffffff) { - writeInt32((value.toInt() << 1).toSigned(32)); - return; - } - writeUint8(0x01); - writeInt64(value); - } - - @pragma('vm:prefer-inline') - void writeTaggedInt64FromInt(int value) { - if (value >= -0x40000000 && value <= 0x3fffffff) { - writeInt32((value << 1).toSigned(32)); - return; - } - writeUint8(0x01); - writeInt64FromInt(value); - } - - @pragma('vm:prefer-inline') - void writeTaggedUint64(Uint64 value) { - if (value >= 0 && value <= 0x7fffffff) { - writeInt32(value.toInt() << 1); - return; - } - writeUint8(0x01); - writeUint64(value); - } - - @pragma('vm:prefer-inline') - void writeTaggedUint64FromInt(int value) { - if (value >= 0 && value <= 0x7fffffff) { - writeInt32(value << 1); - return; - } - writeUint8(0x01); - writeUint64FromInt(value); - } - - @pragma('vm:prefer-inline') - void _writeVarUint64Int(int value) { - var remaining = value; - for (var index = 0; index < 8; index += 1) { - final chunk = remaining & 0x7f; - remaining = remaining >>> 7; - if (remaining == 0) { - _bytes[_offset] = chunk; - _offset += 1; - return; - } - _bytes[_offset] = chunk | 0x80; - _offset += 1; - } - _bytes[_offset] = remaining & 0xff; - _offset += 1; - } -} - -@internal -final class GeneratedReadCursor { - final Buffer _buffer; - final ByteData _view; - int _offset; - - GeneratedReadCursor._( - this._buffer, - this._view, - this._offset, - ); - - factory GeneratedReadCursor.start(Buffer buffer) { - return GeneratedReadCursor._( - buffer, - bufferByteData(buffer), - bufferReaderIndex(buffer), - ); - } - - void finish() { - bufferSetReaderIndex(_buffer, _offset); - } - - @pragma('vm:prefer-inline') - bool readBool() => readUint8() != 0; - - @pragma('vm:prefer-inline') - int readByte() { - final value = _view.getInt8(_offset); - _offset += 1; - return value; - } - - @pragma('vm:prefer-inline') - int readUint8() { - final value = _view.getUint8(_offset); - _offset += 1; - return value; - } - - @pragma('vm:prefer-inline') - int readInt16() { - final value = _view.getInt16(_offset, Endian.little); - _offset += 2; - return value; - } - - @pragma('vm:prefer-inline') - int readUint16() { - final value = _view.getUint16(_offset, Endian.little); - _offset += 2; - return value; - } - - @pragma('vm:prefer-inline') - int readInt32() { - final value = _view.getInt32(_offset, Endian.little); - _offset += 4; - return value; - } - - @pragma('vm:prefer-inline') - int readUint32() { - final value = _view.getUint32(_offset, Endian.little); - _offset += 4; - return value; - } - - @pragma('vm:prefer-inline') - Int64 readInt64() { - final value = readInt64LittleEndian(_view, _offset); - _offset += 8; - return value; - } - - @pragma('vm:prefer-inline') - int readInt64AsInt() { - if (useNativeInt64FastPath) { - final value = _view.getInt64(_offset, Endian.little); - _offset += 8; - return value; - } - return readInt64().toInt(); - } - - @pragma('vm:prefer-inline') - Uint64 readUint64() { - final value = readUint64LittleEndian(_view, _offset); - _offset += 8; - return value; - } - - @pragma('vm:prefer-inline') - int readUint64AsInt() { - if (useNativeInt64FastPath) { - final value = _view.getUint64(_offset, Endian.little); - _offset += 8; - return value; - } - return readUint64().toInt(); - } - - @pragma('vm:prefer-inline') - Float16 readFloat16() => Float16.fromBits(readUint16()); - - @pragma('vm:prefer-inline') - Bfloat16 readBfloat16() => Bfloat16.fromBits(readUint16()); - - @pragma('vm:prefer-inline') - double readFloat32() { - final value = _view.getFloat32(_offset, Endian.little); - _offset += 4; - return value; - } - - @pragma('vm:prefer-inline') - double readFloat64() { - final value = _view.getFloat64(_offset, Endian.little); - _offset += 8; - return value; - } - - @pragma('vm:prefer-inline') - int readVarUint32() { - var shift = 0; - var result = 0; - while (true) { - final byte = readUint8(); - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - } - - @pragma('vm:prefer-inline') - int readVarInt32() { - final value = readVarUint32(); - final decoded = (value >>> 1) ^ -(value & 1); - return useNativeInt64FastPath ? decoded : decoded.toSigned(32); - } - - @pragma('vm:prefer-inline') - Uint64 readVarUint64() { - return readVarUint64Bytes(readUint8); - } - - @pragma('vm:prefer-inline') - int readVarUint64AsInt() { - if (useNativeInt64FastPath) { - return _readVarUint64Int(); - } - return readVarUint64().toInt(); - } - - @pragma('vm:prefer-inline') - Int64 readVarInt64() { - return zigZagDecodeInt64(readVarUint64()); - } - - @pragma('vm:prefer-inline') - int readVarInt64AsInt() { - if (useNativeInt64FastPath) { - final encoded = _readVarUint64Int(); - return (encoded >>> 1) ^ -(encoded & 1); - } - return readVarInt64().toInt(); - } - - @pragma('vm:prefer-inline') - Int64 readTaggedInt64() { - final readIndex = _offset; - final first = _view.getInt32(readIndex, Endian.little); - if ((first & 1) == 0) { - _offset = readIndex + 4; - return Int64(first.toSigned(32) ~/ 2); - } - final value = readInt64LittleEndian(_view, readIndex + 1); - _offset = readIndex + 9; - return value; - } - - @pragma('vm:prefer-inline') - int readTaggedInt64AsInt() { - if (useNativeInt64FastPath) { - final readIndex = _offset; - final first = _view.getInt32(readIndex, Endian.little); - if ((first & 1) == 0) { - _offset = readIndex + 4; - return first >> 1; - } - final value = _view.getInt64(readIndex + 1, Endian.little); - _offset = readIndex + 9; - return value; - } - return readTaggedInt64().toInt(); - } - - @pragma('vm:prefer-inline') - Uint64 readTaggedUint64() { - final readIndex = _offset; - final first = _view.getUint32(readIndex, Endian.little); - if ((first & 1) == 0) { - _offset = readIndex + 4; - return Uint64(first >>> 1); - } - final value = readUint64LittleEndian(_view, readIndex + 1); - _offset = readIndex + 9; - return value; - } - - @pragma('vm:prefer-inline') - int readTaggedUint64AsInt() { - if (useNativeInt64FastPath) { - final readIndex = _offset; - final first = _view.getUint32(readIndex, Endian.little); - if ((first & 1) == 0) { - _offset = readIndex + 4; - return first >>> 1; - } - final value = _view.getUint64(readIndex + 1, Endian.little); - _offset = readIndex + 9; - return value; - } - return readTaggedUint64().toInt(); - } - - @pragma('vm:prefer-inline') - int _readVarUint64Int() { - var shift = 0; - var result = 0; - while (shift < 56) { - final byte = readUint8(); - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | (readUint8() << 56); - } -} @internal final class GeneratedFieldType { diff --git a/dart/packages/fory/lib/src/context/meta_string_reader.dart b/dart/packages/fory/lib/src/context/meta_string_reader.dart index fb63ddc570..40d6d3aedb 100644 --- a/dart/packages/fory/lib/src/context/meta_string_reader.dart +++ b/dart/packages/fory/lib/src/context/meta_string_reader.dart @@ -19,7 +19,7 @@ import 'dart:typed_data'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/meta/meta_string.dart'; import 'package:fory/src/resolver/type_resolver.dart'; import 'package:fory/src/types/int64.dart'; diff --git a/dart/packages/fory/lib/src/context/meta_string_writer.dart b/dart/packages/fory/lib/src/context/meta_string_writer.dart index b662d62182..b994f5dc89 100644 --- a/dart/packages/fory/lib/src/context/meta_string_writer.dart +++ b/dart/packages/fory/lib/src/context/meta_string_writer.dart @@ -19,7 +19,7 @@ import 'dart:collection'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/meta/meta_string.dart'; /// Write-side state for meta-string references in one serialization stream. diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart index 55476e0f0f..261f1dd340 100644 --- a/dart/packages/fory/lib/src/context/read_context.dart +++ b/dart/packages/fory/lib/src/context/read_context.dart @@ -19,7 +19,7 @@ import 'package:meta/meta.dart'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/config.dart'; import 'package:fory/src/context/meta_string_reader.dart'; import 'package:fory/src/context/ref_reader.dart'; diff --git a/dart/packages/fory/lib/src/context/ref_reader.dart b/dart/packages/fory/lib/src/context/ref_reader.dart index 50896cd38d..09aaaf7feb 100644 --- a/dart/packages/fory/lib/src/context/ref_reader.dart +++ b/dart/packages/fory/lib/src/context/ref_reader.dart @@ -17,7 +17,7 @@ * under the License. */ -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/context/ref_writer.dart'; final class RefReader { diff --git a/dart/packages/fory/lib/src/context/ref_writer.dart b/dart/packages/fory/lib/src/context/ref_writer.dart index a9a14ce2a5..864c4f293a 100644 --- a/dart/packages/fory/lib/src/context/ref_writer.dart +++ b/dart/packages/fory/lib/src/context/ref_writer.dart @@ -19,7 +19,7 @@ import 'dart:collection'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; final class RefWriter { static const int nullFlag = -3; diff --git a/dart/packages/fory/lib/src/context/write_context.dart b/dart/packages/fory/lib/src/context/write_context.dart index 279fd44123..a9399818cf 100644 --- a/dart/packages/fory/lib/src/context/write_context.dart +++ b/dart/packages/fory/lib/src/context/write_context.dart @@ -23,7 +23,7 @@ import 'dart:collection'; import 'package:meta/meta.dart'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/config.dart'; import 'package:fory/src/context/meta_string_writer.dart'; import 'package:fory/src/context/ref_writer.dart'; @@ -107,7 +107,7 @@ final class WriteContext { set structWriteSlots(StructWriteSlots? value) { _structWriteSlots = value; } - + int get depth => _depth; /// Records entry into one more nested write frame. diff --git a/dart/packages/fory/lib/src/fory.dart b/dart/packages/fory/lib/src/fory.dart index ca5cffa5ca..da2869bef4 100644 --- a/dart/packages/fory/lib/src/fory.dart +++ b/dart/packages/fory/lib/src/fory.dart @@ -19,7 +19,7 @@ import 'dart:typed_data'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/config.dart'; import 'package:fory/src/context/meta_string_reader.dart'; import 'package:fory/src/context/meta_string_writer.dart'; diff --git a/dart/packages/fory/lib/src/util/int64_platform_web.dart b/dart/packages/fory/lib/src/memory/buffer.dart similarity index 91% rename from dart/packages/fory/lib/src/util/int64_platform_web.dart rename to dart/packages/fory/lib/src/memory/buffer.dart index a492ddc52e..6c0b3fb20a 100644 --- a/dart/packages/fory/lib/src/util/int64_platform_web.dart +++ b/dart/packages/fory/lib/src/memory/buffer.dart @@ -17,4 +17,4 @@ * under the License. */ -const bool useNativeInt64FastPath = false; +export 'buffer_native.dart' if (dart.library.js_interop) 'buffer_web.dart'; diff --git a/dart/packages/fory/lib/src/buffer.dart b/dart/packages/fory/lib/src/memory/buffer_mixin.dart similarity index 70% rename from dart/packages/fory/lib/src/buffer.dart rename to dart/packages/fory/lib/src/memory/buffer_mixin.dart index 4c5dbbaf84..e1f300ab7f 100644 --- a/dart/packages/fory/lib/src/buffer.dart +++ b/dart/packages/fory/lib/src/memory/buffer_mixin.dart @@ -17,45 +17,27 @@ * under the License. */ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -import 'package:fory/src/types/bfloat16.dart'; -import 'package:fory/src/types/float16.dart'; -import 'package:fory/src/types/int64.dart'; -import 'package:fory/src/types/uint64.dart'; -import 'package:fory/src/util/int64_codec.dart'; - -/// A reusable byte buffer with explicit reader and writer indices. -/// -/// Fory uses little-endian fixed-width encodings and varint helpers on top of -/// this buffer. The same buffer can be reused across many operations by calling -/// [clear]. -final class Buffer { - Uint8List _bytes; +// ignore_for_file: use_string_in_part_of_directives + +part of fory.src.memory.buffer; + +mixin _BufferMixin { + late Uint8List _bytes; late ByteData _view; - int _readerIndex; - int _writerIndex; - - /// Creates an empty buffer with [initialCapacity] bytes of storage. - Buffer([int initialCapacity = 256]) - : _bytes = Uint8List(initialCapacity), - _readerIndex = 0, - _writerIndex = 0 { + int _readerIndex = 0; + int _writerIndex = 0; + + void _initBuffer(int initialCapacity) { + _bytes = Uint8List(initialCapacity); _view = ByteData.sublistView(_bytes); } - /// Creates a buffer that reads from and writes into [bytes]. - /// - /// The writer index starts at `bytes.length`, so the wrapped bytes are - /// immediately readable. - Buffer.wrap(Uint8List bytes) - : _bytes = bytes, - _view = ByteData.sublistView(bytes), - _readerIndex = 0, - _writerIndex = bytes.length; + void _wrapBuffer(Uint8List bytes) { + _bytes = bytes; + _view = ByteData.sublistView(bytes); + _readerIndex = 0; + _writerIndex = bytes.length; + } /// Number of unread bytes between the reader and writer indices. int get readableBytes => _writerIndex - _readerIndex; @@ -73,10 +55,7 @@ final class Buffer { /// Replaces the underlying storage with [bytes] and resets both indices. void wrap(Uint8List bytes) { - _bytes = bytes; - _view = ByteData.sublistView(bytes); - _readerIndex = 0; - _writerIndex = bytes.length; + _wrapBuffer(bytes); } /// Ensures there is room for [additionalBytes] bytes past the writer index. @@ -190,34 +169,6 @@ final class Buffer { return value; } - /// Writes a signed little-endian 64-bit integer. - void writeInt64(Int64 value) { - ensureWritable(8); - writeInt64LittleEndian(_view, _writerIndex, value); - _writerIndex += 8; - } - - /// Reads a signed little-endian 64-bit integer. - Int64 readInt64() { - final value = readInt64LittleEndian(_view, _readerIndex); - _readerIndex += 8; - return value; - } - - /// Writes an unsigned little-endian 64-bit integer. - void writeUint64(Uint64 value) { - ensureWritable(8); - writeUint64LittleEndian(_view, _writerIndex, value); - _writerIndex += 8; - } - - /// Reads an unsigned little-endian 64-bit integer. - Uint64 readUint64() { - final value = readUint64LittleEndian(_view, _readerIndex); - _readerIndex += 8; - return value; - } - /// Writes a single-precision floating-point value. void writeFloat32(double value) { ensureWritable(4); @@ -323,74 +274,9 @@ final class Buffer { return ((value >>> 1) ^ -(value & 1)).toSigned(32); } - /// Writes an unsigned 64-bit varint. - void writeVarUint64(Uint64 value) { - writeVarUint64Bytes(value, writeUint8); - } - - /// Reads an unsigned 64-bit varint. - Uint64 readVarUint64() { - return readVarUint64Bytes(readUint8); - } - - /// Writes a zig-zag encoded signed 64-bit varint. - void writeVarInt64(Int64 value) { - writeVarUint64(zigZagEncodeInt64(value)); - } - - /// Reads a zig-zag encoded signed 64-bit varint. - Int64 readVarInt64() { - return zigZagDecodeInt64(readVarUint64()); - } - - /// Writes a tagged signed 64-bit integer. - /// - /// Small values use four bytes. Larger values use a tag byte plus eight data - /// bytes. - void writeTaggedInt64(Int64 value) { - if (value >= -0x40000000 && value <= 0x3fffffff) { - writeInt32((value.toInt() << 1).toSigned(32)); - return; - } - writeUint8(0x01); - writeInt64(value); - } - - /// Reads a signed 64-bit integer written by [writeTaggedInt64]. - Int64 readTaggedInt64() { - final readIndex = _readerIndex; - final first = _view.getInt32(readIndex, Endian.little); - if ((first & 1) == 0) { - _readerIndex = readIndex + 4; - return Int64(first.toSigned(32) ~/ 2); - } - final value = readInt64LittleEndian(_view, readIndex + 1); - _readerIndex = readIndex + 9; - return value; - } + void writeVarUint64(Uint64 value); - /// Writes a tagged unsigned 64-bit integer. - void writeTaggedUint64(Uint64 value) { - if (value >= 0 && value <= 0x7fffffff) { - writeInt32(value.toInt() << 1); - return; - } - writeUint8(0x01); - writeUint64(value); - } - - /// Reads an unsigned 64-bit integer written by [writeTaggedUint64]. - Uint64 readTaggedUint64() { - final readIndex = _readerIndex; - final first = _view.getUint32(readIndex, Endian.little); - if ((first & 1) == 0) { - _readerIndex = readIndex + 4; - return Uint64(first >>> 1); - } - final value = readUint64LittleEndian(_view, readIndex + 1); - _readerIndex = readIndex + 9; - return value; - } + Uint64 readVarUint64(); /// Writes a small unsigned integer using the same varint path as /// [writeVarUint32]. diff --git a/dart/packages/fory/lib/src/memory/buffer_native.dart b/dart/packages/fory/lib/src/memory/buffer_native.dart new file mode 100644 index 0000000000..1fe10406a6 --- /dev/null +++ b/dart/packages/fory/lib/src/memory/buffer_native.dart @@ -0,0 +1,267 @@ +/* + * 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. + */ + +// ignore_for_file: unnecessary_library_name + +library fory.src.memory.buffer; + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +import 'package:fory/src/types/bfloat16.dart'; +import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; + +part 'buffer_mixin.dart'; + +/// A reusable byte buffer with explicit reader and writer indices. +/// +/// Fory uses little-endian fixed-width encodings and varint helpers on top of +/// this buffer. The same buffer can be reused across many operations by calling +/// [clear]. +final class Buffer with _BufferMixin { + /// Creates an empty buffer with [initialCapacity] bytes of storage. + Buffer([int initialCapacity = 256]) { + _initBuffer(initialCapacity); + } + + /// Creates a buffer that reads from and writes into [bytes]. + /// + /// The writer index starts at `bytes.length`, so the wrapped bytes are + /// immediately readable. + Buffer.wrap(Uint8List bytes) { + _wrapBuffer(bytes); + } + + /// Writes a signed little-endian 64-bit integer. + void writeInt64(Int64 value) { + ensureWritable(8); + _view.setInt64(_writerIndex, value.toInt(), Endian.little); + _writerIndex += 8; + } + + /// Writes a signed little-endian 64-bit integer from a Dart [int]. + void writeInt64FromInt(int value) { + ensureWritable(8); + _view.setInt64(_writerIndex, value, Endian.little); + _writerIndex += 8; + } + + /// Reads a signed little-endian 64-bit integer. + Int64 readInt64() { + final value = Int64(_view.getInt64(_readerIndex, Endian.little)); + _readerIndex += 8; + return value; + } + + /// Reads a signed little-endian 64-bit integer as a Dart [int]. + int readInt64AsInt() { + final value = _view.getInt64(_readerIndex, Endian.little); + _readerIndex += 8; + return value; + } + + /// Writes an unsigned little-endian 64-bit integer. + void writeUint64(Uint64 value) { + ensureWritable(8); + _view.setUint32(_writerIndex, value.low32, Endian.little); + _view.setUint32(_writerIndex + 4, value.high32Unsigned, Endian.little); + _writerIndex += 8; + } + + /// Reads an unsigned little-endian 64-bit integer. + Uint64 readUint64() { + final value = Uint64.fromWords( + _view.getUint32(_readerIndex, Endian.little), + _view.getUint32(_readerIndex + 4, Endian.little), + ); + _readerIndex += 8; + return value; + } + + /// Writes an unsigned 64-bit varint. + @override + void writeVarUint64(Uint64 value) { + ensureWritable(10); + var remaining = value; + for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { + _bytes[_writerIndex] = (remaining.low32 & 0x7f) | 0x80; + _writerIndex += 1; + remaining = remaining >> 7; + } + _bytes[_writerIndex] = remaining.toInt(); + _writerIndex += 1; + } + + /// Reads an unsigned 64-bit varint. + @override + Uint64 readVarUint64() { + var shift = 0; + var result = Uint64(0); + while (shift < 56) { + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + result = result | (Uint64(byte & 0x7f) << shift); + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + return result | (Uint64(byte) << 56); + } + + /// Writes a zig-zag encoded signed 64-bit varint. + void writeVarInt64(Int64 value) { + writeVarUint64(Uint64((value << 1) ^ (value >> 63))); + } + + /// Writes a zig-zag encoded signed 64-bit varint from a Dart [int]. + void writeVarInt64FromInt(int value) { + ensureWritable(10); + _writeVarUint64Int((value << 1) ^ (value >> 63)); + } + + /// Reads a zig-zag encoded signed 64-bit varint. + Int64 readVarInt64() { + final encoded = readVarUint64(); + return Int64((encoded >>> 1) ^ -(encoded & 1)); + } + + /// Reads a zig-zag encoded signed 64-bit varint as a Dart [int]. + int readVarInt64AsInt() { + final encoded = _readVarUint64AsInt(); + return (encoded >>> 1) ^ -(encoded & 1); + } + + /// Writes a tagged signed 64-bit integer. + /// + /// Small values use four bytes. Larger values use a tag byte plus eight data + /// bytes. + void writeTaggedInt64(Int64 value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value.toInt() << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64(value); + } + + /// Writes a tagged signed 64-bit integer from a Dart [int]. + void writeTaggedInt64FromInt(int value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64FromInt(value); + } + + /// Reads a signed 64-bit integer written by [writeTaggedInt64]. + Int64 readTaggedInt64() { + final readIndex = _readerIndex; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return Int64(first.toSigned(32) ~/ 2); + } + final value = Int64(_view.getInt64(readIndex + 1, Endian.little)); + _readerIndex = readIndex + 9; + return value; + } + + /// Reads a signed 64-bit integer written by [writeTaggedInt64] as an [int]. + int readTaggedInt64AsInt() { + final readIndex = _readerIndex; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return first >> 1; + } + final value = _view.getInt64(readIndex + 1, Endian.little); + _readerIndex = readIndex + 9; + return value; + } + + /// Writes a tagged unsigned 64-bit integer. + void writeTaggedUint64(Uint64 value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value.toInt() << 1); + return; + } + writeUint8(0x01); + writeUint64(value); + } + + /// Reads an unsigned 64-bit integer written by [writeTaggedUint64]. + Uint64 readTaggedUint64() { + final readIndex = _readerIndex; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return Uint64(first >>> 1); + } + final value = Uint64.fromWords( + _view.getUint32(readIndex + 1, Endian.little), + _view.getUint32(readIndex + 5, Endian.little), + ); + _readerIndex = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + void _writeVarUint64Int(int value) { + var remaining = value; + for (var index = 0; index < 8; index += 1) { + final chunk = remaining & 0x7f; + remaining >>>= 7; + if (remaining == 0) { + _bytes[_writerIndex] = chunk; + _writerIndex += 1; + return; + } + _bytes[_writerIndex] = chunk | 0x80; + _writerIndex += 1; + } + _bytes[_writerIndex] = remaining & 0xff; + _writerIndex += 1; + } + + @pragma('vm:prefer-inline') + int _readVarUint64AsInt() { + var shift = 0; + var result = 0; + while (shift < 56) { + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + return result | (byte << 56); + } +} diff --git a/dart/packages/fory/lib/src/memory/buffer_web.dart b/dart/packages/fory/lib/src/memory/buffer_web.dart new file mode 100644 index 0000000000..8f1d6a224a --- /dev/null +++ b/dart/packages/fory/lib/src/memory/buffer_web.dart @@ -0,0 +1,289 @@ +/* + * 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. + */ + +// ignore_for_file: unnecessary_library_name + +library fory.src.memory.buffer; + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +import 'package:fory/src/types/bfloat16.dart'; +import 'package:fory/src/types/float16.dart'; +import 'package:fory/src/types/int64.dart'; +import 'package:fory/src/types/uint64.dart'; + +part 'buffer_mixin.dart'; + +const int _jsSafeIntMax = 9007199254740991; +const int _jsSafeIntMin = -9007199254740991; + +/// A reusable byte buffer with explicit reader and writer indices. +/// +/// Fory uses little-endian fixed-width encodings and varint helpers on top of +/// this buffer. The same buffer can be reused across many operations by calling +/// [clear]. +final class Buffer with _BufferMixin { + /// Creates an empty buffer with [initialCapacity] bytes of storage. + Buffer([int initialCapacity = 256]) { + _initBuffer(initialCapacity); + } + + /// Creates a buffer that reads from and writes into [bytes]. + /// + /// The writer index starts at `bytes.length`, so the wrapped bytes are + /// immediately readable. + Buffer.wrap(Uint8List bytes) { + _wrapBuffer(bytes); + } + + /// Writes a signed little-endian 64-bit integer. + void writeInt64(Int64 value) { + ensureWritable(8); + _writeInt64Words(_writerIndex, value); + _writerIndex += 8; + } + + /// Writes a signed little-endian 64-bit integer from a Dart [int]. + void writeInt64FromInt(int value) { + ensureWritable(8); + _writeInt64Words(_writerIndex, _int64FromInt(value)); + _writerIndex += 8; + } + + /// Reads a signed little-endian 64-bit integer. + Int64 readInt64() { + final value = _readInt64Words(_readerIndex); + _readerIndex += 8; + return value; + } + + /// Reads a signed little-endian 64-bit integer as a Dart [int]. + int readInt64AsInt() { + final value = _int64ToInt(_readInt64Words(_readerIndex)); + _readerIndex += 8; + return value; + } + + /// Writes an unsigned little-endian 64-bit integer. + void writeUint64(Uint64 value) { + ensureWritable(8); + _writeUint64Words(_writerIndex, value); + _writerIndex += 8; + } + + /// Reads an unsigned little-endian 64-bit integer. + Uint64 readUint64() { + final value = _readUint64Words(_readerIndex); + _readerIndex += 8; + return value; + } + + /// Writes an unsigned 64-bit varint. + @override + void writeVarUint64(Uint64 value) { + ensureWritable(10); + var remaining = value; + for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { + _bytes[_writerIndex] = (remaining.low32 & 0x7f) | 0x80; + _writerIndex += 1; + remaining = remaining >> 7; + } + _bytes[_writerIndex] = remaining.toInt(); + _writerIndex += 1; + } + + /// Reads an unsigned 64-bit varint. + @override + Uint64 readVarUint64() { + var shift = 0; + var result = Uint64(0); + while (shift < 56) { + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + result = result | (Uint64(byte & 0x7f) << shift); + if ((byte & 0x80) == 0) { + return result; + } + shift += 7; + } + final byte = _view.getUint8(_readerIndex); + _readerIndex += 1; + return result | (Uint64(byte) << 56); + } + + /// Writes a zig-zag encoded signed 64-bit varint. + void writeVarInt64(Int64 value) { + writeVarUint64(_zigZagEncodeInt64(value)); + } + + /// Writes a zig-zag encoded signed 64-bit varint from a Dart [int]. + void writeVarInt64FromInt(int value) { + writeVarInt64(_int64FromInt(value)); + } + + /// Reads a zig-zag encoded signed 64-bit varint. + Int64 readVarInt64() { + return _zigZagDecodeInt64(readVarUint64()); + } + + /// Reads a zig-zag encoded signed 64-bit varint as a Dart [int]. + int readVarInt64AsInt() { + return _int64ToInt(readVarInt64()); + } + + /// Writes a tagged signed 64-bit integer. + /// + /// Small values use four bytes. Larger values use a tag byte plus eight data + /// bytes. + void writeTaggedInt64(Int64 value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value.toInt() << 1).toSigned(32)); + return; + } + writeUint8(0x01); + writeInt64(value); + } + + /// Writes a tagged signed 64-bit integer from a Dart [int]. + void writeTaggedInt64FromInt(int value) { + if (value >= -0x40000000 && value <= 0x3fffffff) { + writeInt32((value << 1).toSigned(32)); + return; + } + _checkInt64IntRange(value); + writeUint8(0x01); + writeInt64FromInt(value); + } + + /// Reads a signed 64-bit integer written by [writeTaggedInt64]. + Int64 readTaggedInt64() { + final readIndex = _readerIndex; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return Int64(first.toSigned(32) ~/ 2); + } + final value = _readInt64Words(readIndex + 1); + _readerIndex = readIndex + 9; + return value; + } + + /// Reads a signed 64-bit integer written by [writeTaggedInt64] as an [int]. + int readTaggedInt64AsInt() { + final readIndex = _readerIndex; + final first = _view.getInt32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return first >> 1; + } + final value = _int64ToInt(_readInt64Words(readIndex + 1)); + _readerIndex = readIndex + 9; + return value; + } + + /// Writes a tagged unsigned 64-bit integer. + void writeTaggedUint64(Uint64 value) { + if (value >= 0 && value <= 0x7fffffff) { + writeInt32(value.toInt() << 1); + return; + } + writeUint8(0x01); + writeUint64(value); + } + + /// Reads an unsigned 64-bit integer written by [writeTaggedUint64]. + Uint64 readTaggedUint64() { + final readIndex = _readerIndex; + final first = _view.getUint32(readIndex, Endian.little); + if ((first & 1) == 0) { + _readerIndex = readIndex + 4; + return Uint64(first >>> 1); + } + final value = _readUint64Words(readIndex + 1); + _readerIndex = readIndex + 9; + return value; + } + + @pragma('vm:prefer-inline') + void _writeInt64Words(int offset, Int64 value) { + _view.setUint32(offset, value.low32, Endian.little); + _view.setUint32(offset + 4, value.high32Unsigned, Endian.little); + } + + @pragma('vm:prefer-inline') + Int64 _readInt64Words(int offset) { + return Int64.fromWords( + _view.getUint32(offset, Endian.little), + _view.getInt32(offset + 4, Endian.little), + ); + } + + @pragma('vm:prefer-inline') + void _writeUint64Words(int offset, Uint64 value) { + _view.setUint32(offset, value.low32, Endian.little); + _view.setUint32(offset + 4, value.high32Unsigned, Endian.little); + } + + @pragma('vm:prefer-inline') + Uint64 _readUint64Words(int offset) { + return Uint64.fromWords( + _view.getUint32(offset, Endian.little), + _view.getUint32(offset + 4, Endian.little), + ); + } +} + +@pragma('vm:prefer-inline') +void _checkInt64IntRange(int value) { + if (value < _jsSafeIntMin || value > _jsSafeIntMax) { + throw StateError( + 'Dart int value $value is outside the JS-safe signed int64 range ' + '[$_jsSafeIntMin, $_jsSafeIntMax]. Use Int64 for full 64-bit values ' + 'on web.', + ); + } +} + +@pragma('vm:prefer-inline') +Int64 _int64FromInt(int value) { + _checkInt64IntRange(value); + return Int64(value); +} + +@pragma('vm:prefer-inline') +int _int64ToInt(Int64 value) => value.toInt(); + +@pragma('vm:prefer-inline') +Uint64 _zigZagEncodeInt64(Int64 value) { + final encoded = (value << 1) ^ (value >> 63); + return Uint64.fromWords(encoded.low32, encoded.high32Unsigned); +} + +@pragma('vm:prefer-inline') +Int64 _zigZagDecodeInt64(Uint64 encoded) { + final magnitude = encoded >> 1; + final decoded = Int64.fromWords(magnitude.low32, magnitude.high32Unsigned); + if ((encoded.low32 & 1) == 0) { + return decoded; + } + return -(decoded + 1); +} diff --git a/dart/packages/fory/lib/src/meta/type_meta.dart b/dart/packages/fory/lib/src/meta/type_meta.dart index 3941f8c27e..dc8a2df5e1 100644 --- a/dart/packages/fory/lib/src/meta/type_meta.dart +++ b/dart/packages/fory/lib/src/meta/type_meta.dart @@ -19,7 +19,7 @@ import 'dart:collection'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/config.dart'; import 'package:fory/src/meta/meta_string.dart'; import 'package:fory/src/meta/type_ids.dart'; diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index 981f7fb3a9..20f8e5925f 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -20,7 +20,7 @@ import 'dart:collection'; import 'dart:typed_data'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/codegen/generated_registry.dart'; import 'package:fory/src/config.dart'; import 'package:fory/src/context/meta_string_reader.dart'; diff --git a/dart/packages/fory/lib/src/serializer/map_serializers.dart b/dart/packages/fory/lib/src/serializer/map_serializers.dart index 57ae790303..4f1a35cb57 100644 --- a/dart/packages/fory/lib/src/serializer/map_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/map_serializers.dart @@ -17,7 +17,7 @@ * under the License. */ -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/context/read_context.dart'; import 'package:fory/src/context/ref_writer.dart'; import 'package:fory/src/context/write_context.dart'; diff --git a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart index 3cb449fdd9..053319e40f 100644 --- a/dart/packages/fory/lib/src/serializer/scalar_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/scalar_serializers.dart @@ -24,7 +24,6 @@ import 'package:fory/src/context/write_context.dart'; import 'package:fory/src/serializer/serializer.dart'; import 'package:fory/src/types/int64.dart'; import 'package:fory/src/types/uint64.dart'; -import 'package:fory/src/util/int64_codec.dart'; import 'package:fory/src/util/string_util.dart'; import 'package:fory/src/types/decimal.dart'; @@ -59,6 +58,20 @@ BigInt _decimalMagnitudeFromCanonicalLittleEndian(Uint8List payload) { return magnitude; } +Uint64 _zigZagEncodeInt64(Int64 value) { + final encoded = (value << 1) ^ (value >> 63); + return Uint64.fromWords(encoded.low32, encoded.high32Unsigned); +} + +Int64 _zigZagDecodeInt64(Uint64 encoded) { + final magnitude = encoded >> 1; + final decoded = Int64.fromWords(magnitude.low32, magnitude.high32Unsigned); + if ((encoded.low32 & 1) == 0) { + return decoded; + } + return -(decoded + 1); +} + final class NoneSerializer extends Serializer { const NoneSerializer(); @@ -160,7 +173,7 @@ final class DecimalSerializer extends Serializer { final unscaled = value.unscaledValue; buffer.writeVarInt32(value.scale); if (_canUseSmallDecimalEncoding(unscaled)) { - final zigZag = zigZagEncodeInt64(Int64.fromBigInt(unscaled)); + final zigZag = _zigZagEncodeInt64(Int64.fromBigInt(unscaled)); buffer.writeVarUint64(zigZag << 1); return; } @@ -177,7 +190,7 @@ final class DecimalSerializer extends Serializer { final header = context.buffer.readVarUint64(); if ((header.low32 & 1) == 0) { final zigZag = header >>> 1; - return Decimal(zigZagDecodeInt64(zigZag).toBigInt(), scale); + return Decimal(_zigZagDecodeInt64(zigZag).toBigInt(), scale); } final meta = header >>> 1; diff --git a/dart/packages/fory/lib/src/util/int64_codec.dart b/dart/packages/fory/lib/src/util/int64_codec.dart deleted file mode 100644 index 22b468ea3b..0000000000 --- a/dart/packages/fory/lib/src/util/int64_codec.dart +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 'dart:typed_data'; - -import 'package:fory/src/types/int64.dart'; -import 'package:fory/src/types/uint64.dart'; -import 'package:fory/src/util/int64_platform.dart'; - -void writeInt64LittleEndian(ByteData view, int offset, Int64 value) { - if (useNativeInt64FastPath) { - view.setInt64(offset, value.toInt(), Endian.little); - return; - } - view.setUint32(offset, value.low32, Endian.little); - view.setUint32(offset + 4, value.high32Unsigned, Endian.little); -} - -Int64 readInt64LittleEndian(ByteData view, int offset) { - if (useNativeInt64FastPath) { - return Int64(view.getInt64(offset, Endian.little)); - } - return Int64.fromWords( - view.getUint32(offset, Endian.little), - view.getInt32(offset + 4, Endian.little), - ); -} - -void writeUint64LittleEndian(ByteData view, int offset, Uint64 value) { - view.setUint32(offset, value.low32, Endian.little); - view.setUint32(offset + 4, value.high32Unsigned, Endian.little); -} - -Uint64 readUint64LittleEndian(ByteData view, int offset) { - return Uint64.fromWords( - view.getUint32(offset, Endian.little), - view.getUint32(offset + 4, Endian.little), - ); -} - -Uint64 zigZagEncodeInt64(Int64 value) { - final encoded = (value << 1) ^ (value >> 63); - return Uint64.fromWords(encoded.low32, encoded.high32Unsigned); -} - -Int64 zigZagDecodeInt64(Uint64 encoded) { - final magnitude = encoded >> 1; - final decoded = Int64.fromWords(magnitude.low32, magnitude.high32Unsigned); - if ((encoded.low32 & 1) == 0) { - return decoded; - } - return -(decoded + 1); -} - -void writeVarUint64Bytes(Uint64 value, void Function(int byte) writeByte) { - var remaining = value; - for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { - writeByte((remaining.low32 & 0x7f) | 0x80); - remaining = remaining >> 7; - } - writeByte(remaining.toInt()); -} - -Uint64 readVarUint64Bytes(int Function() readByte) { - var shift = 0; - var result = Uint64(0); - while (shift < 56) { - final byte = readByte(); - result = result | (Uint64(byte & 0x7f) << shift); - if ((byte & 0x80) == 0) { - return result; - } - shift += 7; - } - return result | (Uint64(readByte()) << 56); -} diff --git a/dart/packages/fory/lib/src/util/int64_platform_native.dart b/dart/packages/fory/lib/src/util/int64_platform_native.dart deleted file mode 100644 index 2033e28cc0..0000000000 --- a/dart/packages/fory/lib/src/util/int64_platform_native.dart +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -const bool useNativeInt64FastPath = true; diff --git a/dart/packages/fory/lib/src/util/string_util.dart b/dart/packages/fory/lib/src/util/string_util.dart index 3a3b5f60dc..b274264b80 100644 --- a/dart/packages/fory/lib/src/util/string_util.dart +++ b/dart/packages/fory/lib/src/util/string_util.dart @@ -20,7 +20,7 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; const int stringLatin1Encoding = 0; const int stringUtf16Encoding = 1; diff --git a/dart/packages/fory/test/string_encoding_test.dart b/dart/packages/fory/test/string_encoding_test.dart index 4a5011cc02..e33d08d4ae 100644 --- a/dart/packages/fory/test/string_encoding_test.dart +++ b/dart/packages/fory/test/string_encoding_test.dart @@ -17,7 +17,7 @@ * under the License. */ -import 'package:fory/src/buffer.dart'; +import 'package:fory/src/memory/buffer.dart'; import 'package:fory/src/util/string_util.dart'; import 'package:test/test.dart'; diff --git a/docs/guide/dart/troubleshooting.md b/docs/guide/dart/troubleshooting.md index 9266d05fb8..a3e4a49634 100644 --- a/docs/guide/dart/troubleshooting.md +++ b/docs/guide/dart/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 10 +sidebar_position: 11 id: dart_troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/dart/web-platform-support.md b/docs/guide/dart/web-platform-support.md new file mode 100644 index 0000000000..38e2aa3dde --- /dev/null +++ b/docs/guide/dart/web-platform-support.md @@ -0,0 +1,20 @@ +--- +title: Web Platform Support +sidebar_position: 10 +id: dart_web_platform_support +license: | + 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. +--- From a30ca48803eabed5416508d0fb8c2532ad139d87 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 16:01:16 +0800 Subject: [PATCH 08/14] docs(dart): document web int64 limits --- docs/guide/dart/troubleshooting.md | 43 ++++++ docs/guide/dart/web-platform-support.md | 197 ++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/docs/guide/dart/troubleshooting.md b/docs/guide/dart/troubleshooting.md index a3e4a49634..95e2c17763 100644 --- a/docs/guide/dart/troubleshooting.md +++ b/docs/guide/dart/troubleshooting.md @@ -78,6 +78,48 @@ Checklist: 4. `Timestamp` / `LocalDate` instead of raw `DateTime` for date/time fields. 5. `compatible: true` on **both** sides if using schema evolution. +## Int64 or Uint64 values fail on web + +On Dart VM builds, Dart `int` can represent signed 64-bit values. On Dart web +builds, Dart `int` values are backed by JavaScript numbers and are only precise +inside the JS-safe integer range: + +```text +-9007199254740991 <= value <= 9007199254740991 +``` + +If a generated serializer writes an `int64` field declared as Dart `int`, +web builds reject values outside that range instead of silently writing +corrupted bytes. To exchange full signed 64-bit values on web, declare the +field as Fory's `Int64` wrapper: + +```dart +@ForyStruct() +class LedgerEntry { + LedgerEntry(); + + Int64 sequence = Int64(0); // full signed 64-bit range on VM and web +} +``` + +For unsigned 64-bit values, prefer `Uint64` rather than Dart `int`. Dart `int` +cannot represent the full `uint64` range on either VM or web: + +```dart +@ForyStruct() +class FileBlock { + FileBlock(); + + Uint64 offset = Uint64(0); // full unsigned 64-bit range +} +``` + +`@Int64Type` changes the wire encoding for a Dart `int` field, but it does not +remove the web integer precision limit. Use `Int64` for full-range signed +values and `Uint64` for full-range unsigned values. See +[Web Platform Support](web-platform-support.md) for the full browser support +matrix and migration guidance. + ## Running Tests Locally Main Dart package: @@ -101,3 +143,4 @@ dart test - [Cross-Language](cross-language.md) - [Code Generation](code-generation.md) - [Custom Serializers](custom-serializers.md) +- [Web Platform Support](web-platform-support.md) diff --git a/docs/guide/dart/web-platform-support.md b/docs/guide/dart/web-platform-support.md index 38e2aa3dde..34538c08fc 100644 --- a/docs/guide/dart/web-platform-support.md +++ b/docs/guide/dart/web-platform-support.md @@ -18,3 +18,200 @@ license: | See the License for the specific language governing permissions and limitations under the License. --- + +Fory Dart supports browser and Flutter web builds through generated serializers +and platform-specific runtime implementations. The public API is the same as VM +builds, but web builds have stricter integer precision rules because Dart `int` +is represented by JavaScript numbers. + +## Supported Web Targets + +The Dart runtime supports: + +- Dart applications compiled to JavaScript for browsers. +- Flutter web applications. +- Generated `@ForyStruct` serializers and manually registered serializers. +- Cross-language (`xlang`) payloads produced by other Fory runtimes. + +The Dart runtime does not support Fory native-mode payloads. If a peer runtime +is Java, Go, C++, Rust, Python, C#, JavaScript, or another Fory implementation, +configure that peer to write xlang-compatible payloads before sending data to +Dart. + +## Code Generation Is Required + +Web and Flutter builds cannot rely on `dart:mirrors`. Register generated +serializers explicitly: + +```dart +import 'package:fory/fory.dart'; + +part 'account.fory.dart'; + +@ForyStruct() +class Account { + Account(); + + String name = ''; + Int64 sequence = Int64(0); +} + +void main() { + final fory = Fory(); + AccountFory.register( + fory, + Account, + namespace: 'example', + typeName: 'Account', + ); + + final bytes = fory.serialize(Account()..name = 'web'); + final account = fory.deserialize(bytes); + print(account.name); +} +``` + +Generate the companion file before building or testing: + +```bash +cd dart/packages/fory +dart run build_runner build --delete-conflicting-outputs +``` + +The registration call is the same on VM and web. Do not call private generated +helpers directly. + +## 64-Bit Integer Rules + +Dart VM `int` values are signed 64-bit values. Dart web `int` values are backed +by JavaScript numbers and are precise only in the JS-safe integer range: + +```text +-9007199254740991 <= value <= 9007199254740991 +``` + +Use this rule when choosing field types: + +| Logical value | Recommended Dart field type on web | Notes | +| ---------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Signed 64-bit value within JS-safe range | `int` | Works with default `int64` mapping and `@Int64Type` encodings. | +| Full signed 64-bit range | `Int64` | Preserves values outside the JS-safe range. | +| Unsigned 64-bit value | `Uint64` | Required for values that do not fit in signed or JS-safe Dart `int`. | +| 8/16/32-bit integer | `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32` or annotations | Use wrappers or numeric annotations to match peer runtimes exactly. | + +`@Int64Type` controls the wire encoding of a Dart `int` field: + +```dart +@ForyStruct() +class SafeCounter { + SafeCounter(); + + @Int64Type(encoding: LongEncoding.tagged) + int count = 0; // keep web values inside the JS-safe range +} +``` + +It does not make Dart `int` capable of storing every 64-bit value on web. For +full-range signed values, use `Int64`: + +```dart +@ForyStruct() +class FullRangeCounter { + FullRangeCounter(); + + Int64 count = Int64(0); +} +``` + +For unsigned values, use `Uint64`: + +```dart +@ForyStruct() +class StorageExtent { + StorageExtent(); + + Uint64 byteOffset = Uint64(0); +} +``` + +## Custom Serializers + +Custom serializers can use the same `Buffer`, `WriteContext`, and `ReadContext` +APIs on VM and web. For 64-bit values: + +- Use `buffer.writeInt64(Int64(...))` and `buffer.readInt64()` for full-range + signed 64-bit values. +- Use `buffer.writeUint64(Uint64(...))` and `buffer.readUint64()` for full-range + unsigned 64-bit values. +- Use `writeInt64FromInt`, `writeVarInt64FromInt`, and matching `AsInt` reads + only when the value is intended to be a Dart `int` and therefore must stay + JS-safe on web. + +Example: + +```dart +final class OffsetSerializer extends Serializer { + const OffsetSerializer(); + + @override + void write(WriteContext context, StorageExtent value) { + context.buffer.writeUint64(value.byteOffset); + } + + @override + StorageExtent read(ReadContext context) { + return StorageExtent()..byteOffset = context.buffer.readUint64(); + } +} +``` + +## Collections And Typed Arrays + +`List`, `Set`, `Map`, `Uint8List`, numeric typed arrays, `Int64List`, and +`Uint64List` are supported on web. The `Int64List` and `Uint64List` +implementations preserve 64-bit values without depending on JavaScript integer +precision. Use the Fory wrapper list types when the wire type is `int64_array` +or `uint64_array`. + +## Testing Browser Builds + +Run the package tests in both VM and Chrome when changing code that must work on +web: + +```bash +cd dart/packages/fory +dart run build_runner build --delete-conflicting-outputs +dart test +dart test -p chrome +``` + +If Chrome tests fail with a stale generated file or missing part file, rerun +`build_runner` and then retry the test command from `dart/packages/fory`. + +## Common Web Failures + +### `Dart int value ... is outside the JS-safe signed int64 range` + +The serializer is trying to write a Dart `int` as a signed 64-bit value on web, +but the value is outside the range that JavaScript numbers can represent +exactly. Change the field type to `Int64`, or keep the value inside the JS-safe +range. + +### `Int64 value ... is not a JS-safe int` + +The deserializer read a full-range `Int64`, but the target field or custom +serializer asked for a Dart `int`. Change the field type to `Int64`, or decode +with `readInt64()` instead of an `AsInt` helper. + +### `Uint64 value ... is not a JS-safe int` + +The code is converting a `Uint64` to Dart `int` on web. Keep the value as +`Uint64` unless the application has already validated that it is in the +JS-safe non-negative range. + +## Related Topics + +- [Supported Types](supported-types.md) +- [Field Configuration](field-configuration.md) +- [Code Generation](code-generation.md) +- [Troubleshooting](troubleshooting.md) From f1129be972364bf27cffe002a60e93647bc64d50 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 16:05:43 +0800 Subject: [PATCH 09/14] docs(dart): clarify web serializer registration --- docs/guide/dart/web-platform-support.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/guide/dart/web-platform-support.md b/docs/guide/dart/web-platform-support.md index 34538c08fc..39a14d7b10 100644 --- a/docs/guide/dart/web-platform-support.md +++ b/docs/guide/dart/web-platform-support.md @@ -31,17 +31,12 @@ The Dart runtime supports: - Dart applications compiled to JavaScript for browsers. - Flutter web applications. - Generated `@ForyStruct` serializers and manually registered serializers. -- Cross-language (`xlang`) payloads produced by other Fory runtimes. - -The Dart runtime does not support Fory native-mode payloads. If a peer runtime -is Java, Go, C++, Rust, Python, C#, JavaScript, or another Fory implementation, -configure that peer to write xlang-compatible payloads before sending data to -Dart. ## Code Generation Is Required -Web and Flutter builds cannot rely on `dart:mirrors`. Register generated -serializers explicitly: +Fory Dart uses explicit registration instead of runtime reflection. For +annotated structs, run code generation and register the generated serializer +before serializing or deserializing values: ```dart import 'package:fory/fory.dart'; @@ -78,8 +73,9 @@ cd dart/packages/fory dart run build_runner build --delete-conflicting-outputs ``` -The registration call is the same on VM and web. Do not call private generated -helpers directly. +The registration call is the same on VM and web. Manual serializers use +`registerSerializer(...)`; generated structs use the generated `register` +wrapper. ## 64-Bit Integer Rules From 63c8374c89cb2f9b9a4ee300587e030b7e291e5e Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 16:23:51 +0800 Subject: [PATCH 10/14] fix(dart): decode tagged int64 int helpers on web --- .../src/codegen/generated_cursor_native.dart | 2 +- .../lib/src/codegen/generated_cursor_web.dart | 2 +- .../fory/lib/src/memory/buffer_native.dart | 2 +- .../fory/lib/src/memory/buffer_web.dart | 2 +- dart/packages/fory/test/buffer_test.dart | 138 +++++++++ .../fory/test/signed_serializer_test.dart | 263 ++++++++++++++++++ .../fory/test/unsigned_serializer_test.dart | 58 +++- 7 files changed, 456 insertions(+), 11 deletions(-) create mode 100644 dart/packages/fory/test/signed_serializer_test.dart diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart index 5c9e987a36..eb865a7d71 100644 --- a/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart @@ -259,7 +259,7 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _offset = readIndex + 4; - return first >> 1; + return first.toSigned(32) ~/ 2; } final value = _view.getInt64(readIndex + 1, Endian.little); _offset = readIndex + 9; diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart index cd7dabb302..7d30c3ae92 100644 --- a/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart @@ -236,7 +236,7 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _offset = readIndex + 4; - return first >> 1; + return first.toSigned(32) ~/ 2; } final value = _int64ToInt(_readInt64Words(readIndex + 1)); _offset = readIndex + 9; diff --git a/dart/packages/fory/lib/src/memory/buffer_native.dart b/dart/packages/fory/lib/src/memory/buffer_native.dart index 1fe10406a6..3089853002 100644 --- a/dart/packages/fory/lib/src/memory/buffer_native.dart +++ b/dart/packages/fory/lib/src/memory/buffer_native.dart @@ -196,7 +196,7 @@ final class Buffer with _BufferMixin { final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _readerIndex = readIndex + 4; - return first >> 1; + return first.toSigned(32) ~/ 2; } final value = _view.getInt64(readIndex + 1, Endian.little); _readerIndex = readIndex + 9; diff --git a/dart/packages/fory/lib/src/memory/buffer_web.dart b/dart/packages/fory/lib/src/memory/buffer_web.dart index 8f1d6a224a..9a4428355c 100644 --- a/dart/packages/fory/lib/src/memory/buffer_web.dart +++ b/dart/packages/fory/lib/src/memory/buffer_web.dart @@ -193,7 +193,7 @@ final class Buffer with _BufferMixin { final first = _view.getInt32(readIndex, Endian.little); if ((first & 1) == 0) { _readerIndex = readIndex + 4; - return first >> 1; + return first.toSigned(32) ~/ 2; } final value = _int64ToInt(_readInt64Words(readIndex + 1)); _readerIndex = readIndex + 9; diff --git a/dart/packages/fory/test/buffer_test.dart b/dart/packages/fory/test/buffer_test.dart index b42d14fb30..3eef20b06c 100644 --- a/dart/packages/fory/test/buffer_test.dart +++ b/dart/packages/fory/test/buffer_test.dart @@ -30,6 +30,10 @@ Int64 _i64Pow2(int shift) => Int64(1) << shift; Uint64 _u64Pow2(int shift) => Uint64(1) << shift; +const int _jsSafeIntMax = 9007199254740991; +const int _jsSafeIntMin = -9007199254740991; +const int _jsUnsafeInt = 9007199254740992; + void main() { group('Buffer', () { test('round-trips fixed-width primitives, 16-bit floats, and bytes', () { @@ -324,6 +328,100 @@ void main() { }, ); + test('int64 int helpers match Int64 wrapper encodings at safe boundaries', + () { + const cases = [ + _jsSafeIntMin, + -0x40000001, + -0x40000000, + -1, + 0, + 1, + 0x3fffffff, + 0x40000000, + _jsSafeIntMax, + ]; + + for (final value in cases) { + _expectInt64IntHelperMatchesWrapper( + value: value, + writeInt: (buffer, value) => buffer.writeInt64FromInt(value), + writeWrapper: (buffer, value) => buffer.writeInt64(Int64(value)), + readInt: (buffer) => buffer.readInt64AsInt(), + cursorWriteInt: (cursor, value) => cursor.writeInt64FromInt(value), + cursorWriteWrapper: (cursor, value) => + cursor.writeInt64(Int64(value)), + cursorReadInt: (cursor) => cursor.readInt64AsInt(), + ); + _expectInt64IntHelperMatchesWrapper( + value: value, + writeInt: (buffer, value) => buffer.writeVarInt64FromInt(value), + writeWrapper: (buffer, value) => buffer.writeVarInt64(Int64(value)), + readInt: (buffer) => buffer.readVarInt64AsInt(), + cursorWriteInt: (cursor, value) => cursor.writeVarInt64FromInt(value), + cursorWriteWrapper: (cursor, value) => + cursor.writeVarInt64(Int64(value)), + cursorReadInt: (cursor) => cursor.readVarInt64AsInt(), + ); + _expectInt64IntHelperMatchesWrapper( + value: value, + writeInt: (buffer, value) => buffer.writeTaggedInt64FromInt(value), + writeWrapper: (buffer, value) => + buffer.writeTaggedInt64(Int64(value)), + readInt: (buffer) => buffer.readTaggedInt64AsInt(), + cursorWriteInt: (cursor, value) => + cursor.writeTaggedInt64FromInt(value), + cursorWriteWrapper: (cursor, value) => + cursor.writeTaggedInt64(Int64(value)), + cursorReadInt: (cursor) => cursor.readTaggedInt64AsInt(), + ); + } + }); + + test('web rejects JS-unsafe int64 int helper values', () { + if (!identical(1, 1.0)) { + final buffer = Buffer(); + buffer.writeInt64FromInt(_jsUnsafeInt); + buffer.writeVarInt64FromInt(_jsUnsafeInt); + buffer.writeTaggedInt64FromInt(_jsUnsafeInt); + expect(buffer.readInt64AsInt(), equals(_jsUnsafeInt)); + expect(buffer.readVarInt64AsInt(), equals(_jsUnsafeInt)); + expect(buffer.readTaggedInt64AsInt(), equals(_jsUnsafeInt)); + return; + } + + expect( + () => Buffer().writeInt64FromInt(_jsUnsafeInt), + throwsA(isA()), + ); + expect( + () => Buffer().writeVarInt64FromInt(_jsUnsafeInt), + throwsA(isA()), + ); + expect( + () => Buffer().writeTaggedInt64FromInt(_jsUnsafeInt), + throwsA(isA()), + ); + + final fixed = Buffer()..writeInt64(Int64(_jsUnsafeInt)); + final varint = Buffer()..writeVarInt64(Int64(_jsUnsafeInt)); + final tagged = Buffer()..writeTaggedInt64(Int64(_jsUnsafeInt)); + expect( + () => Buffer.wrap(Uint8List.fromList(fixed.toBytes())).readInt64AsInt(), + throwsA(isA()), + ); + expect( + () => Buffer.wrap(Uint8List.fromList(varint.toBytes())) + .readVarInt64AsInt(), + throwsA(isA()), + ); + expect( + () => Buffer.wrap(Uint8List.fromList(tagged.toBytes())) + .readTaggedInt64AsInt(), + throwsA(isA()), + ); + }); + test('round-trips small varuint helpers', () { const small7Cases = <({int bytes, int value})>[ (bytes: 1, value: 0), @@ -449,6 +547,46 @@ void main() { }); } +void _expectInt64IntHelperMatchesWrapper({ + required int value, + required void Function(Buffer buffer, int value) writeInt, + required void Function(Buffer buffer, int value) writeWrapper, + required int Function(Buffer buffer) readInt, + required void Function(GeneratedWriteCursor cursor, int value) cursorWriteInt, + required void Function(GeneratedWriteCursor cursor, int value) + cursorWriteWrapper, + required int Function(GeneratedReadCursor cursor) cursorReadInt, +}) { + final intBuffer = Buffer(); + writeInt(intBuffer, value); + + final wrapperBuffer = Buffer(); + writeWrapper(wrapperBuffer, value); + expect(intBuffer.toBytes(), orderedEquals(wrapperBuffer.toBytes())); + + final readBuffer = Buffer.wrap(Uint8List.fromList(intBuffer.toBytes())); + expect(readInt(readBuffer), equals(value)); + expect(readBuffer.readableBytes, equals(0)); + + final cursorIntBuffer = Buffer(); + final cursorInt = GeneratedWriteCursor.reserve(cursorIntBuffer, 10); + cursorWriteInt(cursorInt, value); + cursorInt.finish(); + expect(cursorIntBuffer.toBytes(), orderedEquals(wrapperBuffer.toBytes())); + + final cursorWrapperBuffer = Buffer(); + final cursorWrapper = GeneratedWriteCursor.reserve(cursorWrapperBuffer, 10); + cursorWriteWrapper(cursorWrapper, value); + cursorWrapper.finish(); + expect(cursorWrapperBuffer.toBytes(), orderedEquals(wrapperBuffer.toBytes())); + + final cursorReadBuffer = Buffer.wrap(Uint8List.fromList(intBuffer.toBytes())); + final cursorRead = GeneratedReadCursor.start(cursorReadBuffer); + expect(cursorReadInt(cursorRead), equals(value)); + cursorRead.finish(); + expect(cursorReadBuffer.readableBytes, equals(0)); +} + void _expectEncodedIntRoundTrip({ required int value, required int expectedBytes, diff --git a/dart/packages/fory/test/signed_serializer_test.dart b/dart/packages/fory/test/signed_serializer_test.dart new file mode 100644 index 0000000000..182e6c676d --- /dev/null +++ b/dart/packages/fory/test/signed_serializer_test.dart @@ -0,0 +1,263 @@ +/* + * 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 'package:fory/fory.dart'; +import 'package:fory/src/serializer/compatible_struct_metadata.dart'; +import 'package:test/test.dart'; + +part 'signed_serializer_test.fory.dart'; + +const int _jsSafeIntMax = 9007199254740991; +const int _jsSafeIntMin = -9007199254740991; +const int _jsUnsafeInt = 9007199254740992; +final Int64 _int64Min = Int64.parseHex('8000000000000000'); +final Int64 _int64Max = Int64.parseHex('7fffffffffffffff'); + +@ForyStruct() +class SignedFields { + SignedFields(); + + int plainInt = 0; + + @Int64Type(encoding: LongEncoding.varint) + int i64VarInt = 0; + + @Int64Type(encoding: LongEncoding.fixed) + int i64FixedInt = 0; + + @Int64Type(encoding: LongEncoding.tagged) + int i64TaggedInt = 0; + + Int64 i64Default = Int64(0); + + @Int64Type(encoding: LongEncoding.varint) + Int64 i64Var = Int64(0); + + @Int64Type(encoding: LongEncoding.fixed) + Int64 i64Fixed = Int64(0); + + @Int64Type(encoding: LongEncoding.tagged) + Int64 i64Tagged = Int64(0); + + @Int64Type(encoding: LongEncoding.varint) + int? optionalI64VarInt; + + @Int64Type(encoding: LongEncoding.fixed) + Int64? optionalI64Fixed; + + @Int64Type(encoding: LongEncoding.tagged) + Int64? optionalI64Tagged; +} + +@ForyStruct() +class SignedMetadataReader { + SignedMetadataReader(); + + int plainInt = 0; +} + +void _registerSignedFields(Fory fory) { + SignedSerializerTestFory.register( + fory, + SignedFields, + namespace: 'test', + typeName: 'SignedFields', + ); +} + +void _registerSignedMetadataReader(Fory fory) { + SignedSerializerTestFory.register( + fory, + SignedMetadataReader, + namespace: 'test', + typeName: 'SignedFields', + ); +} + +SignedFields _smallSignedFields() { + return SignedFields() + ..plainInt = -1 + ..i64VarInt = -64 + ..i64FixedInt = 63 + ..i64TaggedInt = 0x3fffffff + ..i64Default = Int64(-1) + ..i64Var = Int64(1) + ..i64Fixed = Int64(-0x40000000) + ..i64Tagged = Int64(0x3fffffff) + ..optionalI64VarInt = -128 + ..optionalI64Fixed = Int64(0x40000000) + ..optionalI64Tagged = Int64(-0x40000001); +} + +SignedFields _jsSafeBoundarySignedFields() { + return SignedFields() + ..plainInt = _jsSafeIntMin + ..i64VarInt = _jsSafeIntMax + ..i64FixedInt = _jsSafeIntMin + ..i64TaggedInt = _jsSafeIntMax + ..i64Default = Int64(_jsSafeIntMin) + ..i64Var = Int64(_jsSafeIntMax) + ..i64Fixed = Int64(_jsSafeIntMin) + ..i64Tagged = Int64(_jsSafeIntMax) + ..optionalI64VarInt = _jsSafeIntMin + ..optionalI64Fixed = Int64(_jsSafeIntMax) + ..optionalI64Tagged = Int64(_jsSafeIntMin); +} + +SignedFields _fullRangeWrapperSignedFields() { + return SignedFields() + ..plainInt = 0 + ..i64VarInt = -0x40000001 + ..i64FixedInt = 0x40000000 + ..i64TaggedInt = -0x40000001 + ..i64Default = _int64Min + ..i64Var = _int64Max + ..i64Fixed = _int64Min + ..i64Tagged = _int64Max + ..optionalI64VarInt = 0x40000000 + ..optionalI64Fixed = _int64Max + ..optionalI64Tagged = _int64Min; +} + +SignedFields _nullSignedFields() { + return SignedFields() + ..plainInt = 1 + ..i64VarInt = 2 + ..i64FixedInt = 3 + ..i64TaggedInt = 4 + ..i64Default = Int64(5) + ..i64Var = Int64(6) + ..i64Fixed = Int64(7) + ..i64Tagged = Int64(8) + ..optionalI64VarInt = null + ..optionalI64Fixed = null + ..optionalI64Tagged = null; +} + +void _expectSignedFieldsEqual(SignedFields actual, SignedFields expected) { + expect(actual.plainInt, equals(expected.plainInt)); + expect(actual.i64VarInt, equals(expected.i64VarInt)); + expect(actual.i64FixedInt, equals(expected.i64FixedInt)); + expect(actual.i64TaggedInt, equals(expected.i64TaggedInt)); + expect(actual.i64Default, equals(expected.i64Default)); + expect(actual.i64Var, equals(expected.i64Var)); + expect(actual.i64Fixed, equals(expected.i64Fixed)); + expect(actual.i64Tagged, equals(expected.i64Tagged)); + expect(actual.optionalI64VarInt, equals(expected.optionalI64VarInt)); + expect(actual.optionalI64Fixed, equals(expected.optionalI64Fixed)); + expect(actual.optionalI64Tagged, equals(expected.optionalI64Tagged)); +} + +int _remoteFieldTypeId(Object value, String identifier) { + final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value); + expect(remoteTypeDef, isNotNull); + final field = remoteTypeDef!.fields.firstWhere( + (field) => field.identifier == identifier, + ); + return field.fieldType.typeId; +} + +bool _remoteFieldNullable(Object value, String identifier) { + final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value); + expect(remoteTypeDef, isNotNull); + final field = remoteTypeDef!.fields.firstWhere( + (field) => field.identifier == identifier, + ); + return field.fieldType.nullable; +} + +void main() { + group('signed generated fields', () { + test('round trips int and Int64 encoding edge cases', () { + final fory = Fory(); + _registerSignedFields(fory); + + for (final value in [ + _smallSignedFields(), + _jsSafeBoundarySignedFields(), + _fullRangeWrapperSignedFields(), + _nullSignedFields(), + ]) { + final roundTrip = fory.deserialize(fory.serialize(value)); + _expectSignedFieldsEqual(roundTrip, value); + } + }); + + test('compatible metadata records signed wire types and nullability', () { + final writer = Fory(compatible: true); + final reader = Fory(compatible: true); + _registerSignedFields(writer); + _registerSignedMetadataReader(reader); + + final roundTrip = reader.deserialize( + writer.serialize(_fullRangeWrapperSignedFields()), + ); + expect(roundTrip.plainInt, equals(0)); + + expect( + _remoteFieldTypeId(roundTrip, 'plain_int'), + equals(TypeIds.varInt64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i64_var_int'), + equals(TypeIds.varInt64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i64_fixed_int'), + equals(TypeIds.int64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i64_tagged_int'), + equals(TypeIds.taggedInt64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i64_default'), + equals(TypeIds.varInt64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i64_var'), + equals(TypeIds.varInt64), + ); + expect(_remoteFieldTypeId(roundTrip, 'i64_fixed'), equals(TypeIds.int64)); + expect( + _remoteFieldTypeId(roundTrip, 'i64_tagged'), + equals(TypeIds.taggedInt64), + ); + + expect(_remoteFieldNullable(roundTrip, 'plain_int'), isFalse); + expect(_remoteFieldNullable(roundTrip, 'optional_i64_var_int'), isTrue); + expect(_remoteFieldNullable(roundTrip, 'optional_i64_fixed'), isTrue); + expect(_remoteFieldNullable(roundTrip, 'optional_i64_tagged'), isTrue); + }); + + test('web rejects JS-unsafe Dart int fields instead of corrupting bytes', + () { + final fory = Fory(); + _registerSignedFields(fory); + final value = _smallSignedFields()..i64FixedInt = _jsUnsafeInt; + + if (identical(1, 1.0)) { + expect(() => fory.serialize(value), throwsA(isA())); + } else { + final roundTrip = fory.deserialize(fory.serialize(value)); + _expectSignedFieldsEqual(roundTrip, value); + } + }); + }); +} diff --git a/dart/packages/fory/test/unsigned_serializer_test.dart b/dart/packages/fory/test/unsigned_serializer_test.dart index 1f7548e674..169c5602ca 100644 --- a/dart/packages/fory/test/unsigned_serializer_test.dart +++ b/dart/packages/fory/test/unsigned_serializer_test.dart @@ -102,6 +102,42 @@ void _registerUnsignedMetadataReader(Fory fory) { ); } +UnsignedFields _smallUnsignedFields() { + return UnsignedFields() + ..u8 = 0 + ..u16 = 1 + ..u32Var = 0 + ..u32Fixed = 1 + ..u64Var = Uint64(0) + ..u64Fixed = Uint64(1) + ..u64Tagged = Uint64(0) + ..u8Nullable = 1 + ..u16Nullable = 0 + ..u32VarNullable = 1 + ..u32FixedNullable = 0 + ..u64VarNullable = Uint64(1) + ..u64FixedNullable = Uint64(0) + ..u64TaggedNullable = Uint64(1); +} + +UnsignedFields _taggedBoundaryUnsignedFields() { + return UnsignedFields() + ..u8 = 0x7f + ..u16 = 0x7fff + ..u32Var = 0x7fffffff + ..u32Fixed = 0x80000000 + ..u64Var = Uint64(0x7fffffff) + ..u64Fixed = Uint64(0x80000000) + ..u64Tagged = Uint64(0x7fffffff) + ..u8Nullable = 0x80 + ..u16Nullable = 0x8000 + ..u32VarNullable = 0x80000000 + ..u32FixedNullable = 0x7fffffff + ..u64VarNullable = Uint64(0x80000000) + ..u64FixedNullable = Uint64(0x7fffffff) + ..u64TaggedNullable = Uint64(0x80000000); +} + UnsignedFields _midpointUnsignedFields() { return UnsignedFields() ..u8 = 0x80 @@ -156,7 +192,8 @@ UnsignedFields _nullUnsignedFields() { ..u64TaggedNullable = null; } -void _expectUnsignedFieldsEqual(UnsignedFields actual, UnsignedFields expected) { +void _expectUnsignedFieldsEqual( + UnsignedFields actual, UnsignedFields expected) { expect(actual.u8, equals(expected.u8)); expect(actual.u16, equals(expected.u16)); expect(actual.u32Var, equals(expected.u32Var)); @@ -193,16 +230,19 @@ bool _remoteFieldNullable(Object value, String identifier) { void main() { group('unsigned generated fields', () { - test('round trips midpoint, max, and null boundary cases', () { + test('round trips small, threshold, midpoint, max, and null cases', () { final fory = Fory(); _registerUnsignedFields(fory); for (final value in [ + _smallUnsignedFields(), + _taggedBoundaryUnsignedFields(), _midpointUnsignedFields(), _maxUnsignedFields(), _nullUnsignedFields(), ]) { - final roundTrip = fory.deserialize(fory.serialize(value)); + final roundTrip = + fory.deserialize(fory.serialize(value)); _expectUnsignedFieldsEqual(roundTrip, value); } }); @@ -221,10 +261,14 @@ void main() { expect(_remoteFieldTypeId(roundTrip, 'u8'), equals(TypeIds.uint8)); expect(_remoteFieldTypeId(roundTrip, 'u16'), equals(TypeIds.uint16)); - expect(_remoteFieldTypeId(roundTrip, 'u32_var'), equals(TypeIds.varUint32)); - expect(_remoteFieldTypeId(roundTrip, 'u32_fixed'), equals(TypeIds.uint32)); - expect(_remoteFieldTypeId(roundTrip, 'u64_var'), equals(TypeIds.varUint64)); - expect(_remoteFieldTypeId(roundTrip, 'u64_fixed'), equals(TypeIds.uint64)); + expect( + _remoteFieldTypeId(roundTrip, 'u32_var'), equals(TypeIds.varUint32)); + expect( + _remoteFieldTypeId(roundTrip, 'u32_fixed'), equals(TypeIds.uint32)); + expect( + _remoteFieldTypeId(roundTrip, 'u64_var'), equals(TypeIds.varUint64)); + expect( + _remoteFieldTypeId(roundTrip, 'u64_fixed'), equals(TypeIds.uint64)); expect( _remoteFieldTypeId(roundTrip, 'u64_tagged'), equals(TypeIds.taggedUint64), From 28ecdf5fffed3f8579bdba06a40e9989458df2f0 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 17:49:27 +0800 Subject: [PATCH 11/14] update review skill --- .agents/skills/fory-code-review/SKILL.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.agents/skills/fory-code-review/SKILL.md b/.agents/skills/fory-code-review/SKILL.md index 8e5896e974..5085c57b9e 100644 --- a/.agents/skills/fory-code-review/SKILL.md +++ b/.agents/skills/fory-code-review/SKILL.md @@ -11,21 +11,34 @@ Find the highest-value bugs, regressions, and missing verification in Apache For ## Start Here -1. If the target is a GitHub PR, create a new local git worktree for the review before checking out or fetching the PR branch. -2. Do not switch the current branch or reuse the current worktree for PR review unless the user explicitly asks for that. -3. If reviewing against main, run `git fetch apache main` before diffing. -4. Inspect the changed files first and cluster them by subsystem. -5. Load only the references needed for the touched areas: +1. Always perform the code review in a subagent. The main agent coordinates scope, gathers the subagent's findings, sanity-checks them, and reports the final review. +2. Reuse the same review subagent for later review passes on the same feature unless the user explicitly asks to review that feature in a new subagent. +3. Do not reuse a review subagent across different features; start a new subagent for each distinct feature or review topic. +4. The review subagent must be read-only: it must not write code, apply patches, create commits, push branches, fix tests, or update docs. It sends review findings or an explicit no-findings result back to the caller. +5. If the target is a GitHub PR, create a new local git worktree for the review before checking out or fetching the PR branch. +6. Do not switch the current branch or reuse the current worktree for PR review unless the user explicitly asks for that. +7. If reviewing against main, run `git fetch apache main` before diffing. +8. Inspect the changed files first and cluster them by subsystem. +9. Load only the references needed for the touched areas: - `references/review-checklist.md` - `references/lesson-derived-red-flags.md` - `references/validation-command-matrix.md` - matching runtime docs under `../../languages/*.md` when the patch is language-specific +## Subagent Reuse + +- Treat each feature under review as the reuse key. A feature may be a PR, issue, branch, commit range, local diff topic, or clearly named subsystem change. +- When the user asks for another pass on the same feature, send the updated context to the existing review subagent and ask it to continue from its prior review state. +- When the user asks to review a different feature, spawn a fresh review subagent even if the same files, language, or subsystem are involved. +- If the user explicitly requests a new subagent for the same feature, honor that request and do not reuse the prior subagent. +- Keep implementation work, CI fixing, and non-review exploration out of the review subagent. The review subagent reports comments to the caller; the caller decides whether any separate implementation task should happen. + ## Review Workflow 1. Define the review target. - Determine whether the user wants a review of a PR, branch, commit range, or local diff. +- Assign the target to the correct review subagent using the Subagent Reuse rules before inspecting code. - For a GitHub PR, create and use a dedicated local worktree for the review. Keep the current worktree and branch unchanged unless the user explicitly requests otherwise. - In that worktree, fetch the PR head and review there instead of checking out the PR branch in the current workspace. - Prefer `git diff --stat` first, then inspect the full patch only for touched subsystems. @@ -63,6 +76,7 @@ Find the highest-value bugs, regressions, and missing verification in Apache For ## Hard Rules - Do not lead with style nits when there are correctness or verification risks. +- Do not write code, edit files, apply patches, commit, push, or fix tests from the review subagent. - Treat benchmark-shape tricks, payload-specific caches, and methodology changes as real findings. - Treat undocumented public API additions, compatibility shims, and one-line wrapper growth as findings when they increase maintenance surface without clear need. - Treat protocol or performance claims without verification evidence as incomplete. From 4890322e12377c1ab8763e1d9db14c1febf886b2 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 18:43:19 +0800 Subject: [PATCH 12/14] refine dart support --- dart/packages/fory/README.md | 24 +- .../fory/lib/src/codegen/fory_generator.dart | 36 ++- .../src/codegen/generated_cursor_native.dart | 64 ++-- .../lib/src/codegen/generated_cursor_web.dart | 14 + .../fory/lib/src/context/read_context.dart | 13 +- dart/packages/fory/lib/src/fory.dart | 7 +- .../fory/lib/src/memory/buffer_mixin.dart | 2 +- .../fory/lib/src/memory/buffer_native.dart | 25 +- .../fory/lib/src/resolver/type_resolver.dart | 25 +- .../src/serializer/primitive_serializers.dart | 47 ++- .../fory/lib/src/types/int64_web.dart | 17 +- .../fory/lib/src/types/uint64_web.dart | 17 +- dart/packages/fory/test/buffer_test.dart | 117 +++++++ .../fory/test/collection_serializer_test.dart | 65 ++++ .../test/int64_uint64_arithmetic_test.dart | 48 +++ .../fory/test/numeric_wrapper_test.dart | 141 +++++++++ ...calar_and_typed_array_serializer_test.dart | 46 ++- .../fory/test/signed_serializer_test.dart | 221 ++++++++++++- .../fory/test/unsigned_serializer_test.dart | 298 +++++++++++++++++- docs/compiler/generated-code.md | 11 +- docs/guide/dart/cross-language.md | 2 +- docs/guide/dart/custom-serializers.md | 4 +- docs/guide/dart/field-configuration.md | 5 +- docs/guide/dart/index.md | 28 +- docs/guide/dart/supported-types.md | 30 +- docs/guide/dart/web-platform-support.md | 25 +- .../xlang_implementation_guide.md | 12 +- 27 files changed, 1197 insertions(+), 147 deletions(-) diff --git a/dart/packages/fory/README.md b/dart/packages/fory/README.md index ee4f453c7b..a44cbdd234 100644 --- a/dart/packages/fory/README.md +++ b/dart/packages/fory/README.md @@ -9,12 +9,13 @@ cases. ## Features - Cross-language serialization with the Fory xlang format +- Dart VM/AOT, Flutter, and web platform support - Generated serializers for annotated structs and enums - Compatible mode for schema evolution - Optional reference tracking for shared and circular object graphs - Manual serializers for external types, custom payloads, and unions -- Explicit xlang value wrappers such as `Int32`, `Uint32`, `Float16`, - `Bfloat16`, `Float32`, `LocalDate`, and `Timestamp`, plus `Duration` +- Explicit xlang value class such as `Int32`, `Int64`, `Uint32`, `Uint64`, + `Float16`, `Bfloat16`, `Float32`, `LocalDate`, and `Timestamp`, plus `Duration` support ## Getting Started @@ -193,13 +194,13 @@ final class PersonSerializer extends Serializer { void write(WriteContext context, Person value) { final buffer = context.buffer; buffer.writeUtf8(value.name); - buffer.writeInt64(value.age); + buffer.writeInt64FromInt(value.age); } @override Person read(ReadContext context) { final buffer = context.buffer; - return Person(buffer.readUtf8(), buffer.readInt64()); + return Person(buffer.readUtf8(), buffer.readInt64AsInt()); } } @@ -222,9 +223,9 @@ void main() { Dart has no native fixed-width 8/16/32-bit integer, unsigned 64-bit integer, or single-precision float types. Fory Dart provides thin wrapper types -(`Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`, `Uint64`, `Float16`, -`Bfloat16`, `Float32`) imported from `package:fory/fory.dart` to represent -these xlang wire types. For 16-bit floating-point arrays, Dart exposes +(`Int8`, `Int16`, `Int32`, `Int64`, `Uint8`, `Uint16`, `Uint32`, `Uint64`, +`Float16`, `Bfloat16`, `Float32`) imported from `package:fory/fory.dart` to +represent these xlang wire types. For 16-bit floating-point arrays, Dart exposes `Float16List` and `Bfloat16List` as contiguous fixed-length buffers. | Fory xlang type | Dart type | @@ -233,7 +234,7 @@ these xlang wire types. For 16-bit floating-point arrays, Dart exposes | int8 | `fory.Int8` (wrapper) | | int16 | `fory.Int16` (wrapper) | | int32 | `fory.Int32` (wrapper) | -| int64 | `int` | +| int64 | `int` or `fory.Int64` | | uint8 | `fory.Uint8` (wrapper) | | uint16 | `fory.Uint16` (wrapper) | | uint32 | `fory.Uint32` (wrapper) | @@ -257,6 +258,9 @@ these xlang wire types. For 16-bit floating-point arrays, Dart exposes | int16_array | `Int16List` | | int32_array | `Int32List` | | int64_array | `Int64List` | +| uint16_array | `Uint16List` | +| uint32_array | `Uint32List` | +| uint64_array | `Uint64List` | | float16_array | `Float16List` | | bfloat16_array | `Bfloat16List` | | float32_array | `Float32List` | @@ -276,8 +280,8 @@ The main exported API includes: annotations - `Int32Type`, `Int64Type`, `Uint32Type`, `Uint64Type` — numeric encoding overrides -- Numeric wrappers: `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`, - `Uint64`, `Float16`, `Bfloat16`, `Float32` +- Numeric wrappers: `Int8`, `Int16`, `Int32`, `Int64`, `Uint8`, `Uint16`, + `Uint32`, `Uint64`, `Float16`, `Bfloat16`, `Float32` - Temporal types: `LocalDate`, `Timestamp`, `Duration` ## Cross-Language Notes diff --git a/dart/packages/fory/lib/src/codegen/fory_generator.dart b/dart/packages/fory/lib/src/codegen/fory_generator.dart index a2860e550c..c6e9ad5810 100644 --- a/dart/packages/fory/lib/src/codegen/fory_generator.dart +++ b/dart/packages/fory/lib/src/codegen/fory_generator.dart @@ -426,12 +426,14 @@ final class ForyGenerator extends Generator { output.writeln(); for (var index = 0; index < structSpec.fields.length; index += 1) { final field = structSpec.fields[index]; + final fieldValue = + _generatedFieldInfoWriteValueExpression(field, 'value.${field.name}'); output ..writeln( 'void _write${structSpec.name}Field$index(WriteContext context, GeneratedStructFieldInfo field, ${structSpec.name} value) {', ) ..writeln( - ' writeGeneratedStructFieldInfoValue(context, field, value.${field.name});', + ' writeGeneratedStructFieldInfoValue(context, field, $fieldValue);', ) ..writeln('}') ..writeln(); @@ -541,8 +543,12 @@ final class ForyGenerator extends Generator { ' ${_directGeneratedWriteStatement(field, 'value.${field.name}')};', ); } else { + final fieldValue = _generatedFieldInfoWriteValueExpression( + field, + 'value.${field.name}', + ); output.writeln( - ' writeGeneratedStructFieldInfoValue(context, fields[$index], value.${field.name});', + ' writeGeneratedStructFieldInfoValue(context, fields[$index], $fieldValue);', ); } final directCursorEndRun = directCursorRunByEnd[index]; @@ -1652,6 +1658,28 @@ GeneratedFieldType( } } + String _generatedFieldInfoWriteValueExpression( + _GeneratedFieldSpec field, + String valueExpression, + ) { + if (!_withoutNullability(field.type).isDartCoreInt) { + return valueExpression; + } + final wrapper = switch (field.fieldType.typeId) { + TypeIds.int8 => 'Int8', + TypeIds.int16 => 'Int16', + TypeIds.int32 || TypeIds.varInt32 => 'Int32', + _ => null, + }; + if (wrapper == null) { + return valueExpression; + } + if (_isNullable(field.type)) { + return '$valueExpression == null ? null : $wrapper($valueExpression!)'; + } + return '$wrapper($valueExpression)'; + } + String _nullExpression( DartType type, { required String errorTarget, @@ -2167,9 +2195,9 @@ GeneratedFieldType( case 'Uint16': return TypeIds.uint16; case 'Uint32': - return TypeIds.uint32; + return TypeIds.varUint32; case 'Uint64': - return TypeIds.uint64; + return TypeIds.varUint64; case 'Int64': return TypeIds.varInt64; case 'Float16': diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart index eb865a7d71..482d31d15c 100644 --- a/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_native.dart @@ -55,43 +55,30 @@ final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { @pragma('vm:prefer-inline') void writeUint64(Uint64 value) { - _view.setUint32(_offset, value.low32, Endian.little); - _view.setUint32(_offset + 4, value.high32Unsigned, Endian.little); + _view.setInt64(_offset, value.value, Endian.little); _offset += 8; } @pragma('vm:prefer-inline') void writeUint64FromInt(int value) { - _view.setUint32(_offset, value & 0xffffffff, Endian.little); - _view.setUint32(_offset + 4, (value >> 32) & 0xffffffff, Endian.little); + _view.setInt64(_offset, value.toSigned(64), Endian.little); _offset += 8; } @pragma('vm:prefer-inline') @override void writeVarUint64(Uint64 value) { - var remaining = value; - for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { - _bytes[_offset] = (remaining.low32 & 0x7f) | 0x80; - _offset += 1; - remaining = remaining >> 7; - } - _bytes[_offset] = remaining.toInt(); - _offset += 1; + _writeVarUint64Int(value.value); } @pragma('vm:prefer-inline') void writeVarUint64FromInt(int value) { - if (value < 0) { - writeVarUint64(Uint64(value)); - return; - } - _writeVarUint64Int(value); + _writeVarUint64Int(value.toSigned(64)); } @pragma('vm:prefer-inline') void writeVarInt64(Int64 value) { - writeVarUint64(Uint64((value << 1) ^ (value >> 63))); + _writeVarUint64Int((value << 1) ^ (value >> 63)); } @pragma('vm:prefer-inline') @@ -182,10 +169,7 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { @pragma('vm:prefer-inline') Uint64 readUint64() { - final value = Uint64.fromWords( - _view.getUint32(_offset, Endian.little), - _view.getUint32(_offset + 4, Endian.little), - ); + final value = Uint64(_view.getInt64(_offset, Endian.little)); _offset += 8; return value; } @@ -193,6 +177,11 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { @pragma('vm:prefer-inline') int readUint64AsInt() { final value = _view.getUint64(_offset, Endian.little); + if ((_view.getUint32(_offset + 4, Endian.little) & 0x80000000) != 0) { + throw StateError( + 'Uint64 value $value is not representable as a native int.', + ); + } _offset += 8; return value; } @@ -225,7 +214,14 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { } shift += 7; } - return result | (readUint8() << 56); + final byte = readUint8(); + final value = result | (byte << 56); + if ((byte & 0x80) != 0) { + throw StateError( + 'Uint64 value $value is not representable as a native int.', + ); + } + return value; } @pragma('vm:prefer-inline') @@ -236,7 +232,17 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { @pragma('vm:prefer-inline') int readVarInt64AsInt() { - final encoded = readVarUint64AsInt(); + var shift = 0; + var encoded = 0; + while (shift < 56) { + final byte = readUint8(); + encoded |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + return (encoded >>> 1) ^ -(encoded & 1); + } + shift += 7; + } + encoded |= readUint8() << 56; return (encoded >>> 1) ^ -(encoded & 1); } @@ -274,10 +280,7 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { _offset = readIndex + 4; return Uint64(first >>> 1); } - final value = Uint64.fromWords( - _view.getUint32(readIndex + 1, Endian.little), - _view.getUint32(readIndex + 5, Endian.little), - ); + final value = Uint64(_view.getInt64(readIndex + 1, Endian.little)); _offset = readIndex + 9; return value; } @@ -291,6 +294,11 @@ final class GeneratedReadCursor with _GeneratedReadCursorMixin { return first >>> 1; } final value = _view.getUint64(readIndex + 1, Endian.little); + if ((_view.getUint32(readIndex + 5, Endian.little) & 0x80000000) != 0) { + throw StateError( + 'Uint64 value $value is not representable as a native int.', + ); + } _offset = readIndex + 9; return value; } diff --git a/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart index 7d30c3ae92..d58a518769 100644 --- a/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart +++ b/dart/packages/fory/lib/src/codegen/generated_cursor_web.dart @@ -64,6 +64,7 @@ final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { @pragma('vm:prefer-inline') void writeUint64FromInt(int value) { + _checkUint64IntRange(value); _writeUint64Words(_offset, Uint64(value)); _offset += 8; } @@ -83,6 +84,7 @@ final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { @pragma('vm:prefer-inline') void writeVarUint64FromInt(int value) { + _checkUint64IntRange(value); writeVarUint64(Uint64(value)); } @@ -133,6 +135,7 @@ final class GeneratedWriteCursor with _GeneratedWriteCursorMixin { writeInt32(value << 1); return; } + _checkUint64IntRange(value); writeUint8(0x01); writeUint64FromInt(value); } @@ -303,6 +306,17 @@ Int64 _int64FromInt(int value) { return Int64(value); } +@pragma('vm:prefer-inline') +void _checkUint64IntRange(int value) { + if (value < 0 || value > _jsSafeIntMax) { + throw StateError( + 'Dart int value $value is outside the JS-safe unsigned uint64 int ' + 'field range [0, $_jsSafeIntMax]. Use Uint64 for full unsigned ' + '64-bit values on web.', + ); + } +} + @pragma('vm:prefer-inline') int _int64ToInt(Int64 value) => value.toInt(); diff --git a/dart/packages/fory/lib/src/context/read_context.dart b/dart/packages/fory/lib/src/context/read_context.dart index 261f1dd340..ebec1f320c 100644 --- a/dart/packages/fory/lib/src/context/read_context.dart +++ b/dart/packages/fory/lib/src/context/read_context.dart @@ -228,6 +228,17 @@ final class ReadContext { /// Reads a nullable value using Ref semantics and wire type metadata. Object? readRef() { + return _readRefWithResolved((resolved) => resolved); + } + + /// Reads a root value using Ref semantics and expected root type [T]. + Object? readRefAs() { + return _readRefWithResolved( + (resolved) => _typeResolver.resolveExpectedRootWireType(resolved), + ); + } + + Object? _readRefWithResolved(TypeInfo Function(TypeInfo) resolveRootType) { final flag = _refReader.tryPreserveRefId(_buffer); final preservedRefId = flag >= RefWriter.refValueFlag ? flag : null; if (flag == RefWriter.nullFlag) { @@ -236,7 +247,7 @@ final class ReadContext { if (flag == RefWriter.refFlag) { return _refReader.getReadRef(); } - final resolved = _readTypeMeta(); + final resolved = resolveRootType(_readTypeMeta()); final rootPreservedRefId = preservedRefId == null && flag == RefWriter.notNullValueFlag && _depth == 0 && diff --git a/dart/packages/fory/lib/src/fory.dart b/dart/packages/fory/lib/src/fory.dart index da2869bef4..72b2e64b97 100644 --- a/dart/packages/fory/lib/src/fory.dart +++ b/dart/packages/fory/lib/src/fory.dart @@ -124,6 +124,11 @@ final class Fory { /// Deserializes a value from [buffer] and checks that it is assignable to /// [T]. /// + /// The wire metadata normally determines the decoded value type. Supplying + /// [T] also preserves typed root `Int64` values for varint64 payloads so + /// callers can distinguish them from plain `int` roots where the platform + /// representation supports that distinction. + /// /// Only xlang payloads are supported. This method consumes bytes from the /// current reader position of [buffer]. T deserializeFrom(Buffer buffer) { @@ -143,7 +148,7 @@ final class Fory { 'Only xlang payloads are supported by the Dart runtime.', ); } - final value = _readContext.readRef(); + final value = _readContext.readRefAs(); if (value is T) { return value; } diff --git a/dart/packages/fory/lib/src/memory/buffer_mixin.dart b/dart/packages/fory/lib/src/memory/buffer_mixin.dart index e1f300ab7f..8466c9f8cc 100644 --- a/dart/packages/fory/lib/src/memory/buffer_mixin.dart +++ b/dart/packages/fory/lib/src/memory/buffer_mixin.dart @@ -64,7 +64,7 @@ mixin _BufferMixin { if (required <= _bytes.length) { return; } - var newLength = _bytes.length; + var newLength = _bytes.isEmpty ? 1 : _bytes.length; while (newLength < required) { newLength *= 2; } diff --git a/dart/packages/fory/lib/src/memory/buffer_native.dart b/dart/packages/fory/lib/src/memory/buffer_native.dart index 3089853002..31fa8c1d18 100644 --- a/dart/packages/fory/lib/src/memory/buffer_native.dart +++ b/dart/packages/fory/lib/src/memory/buffer_native.dart @@ -83,17 +83,13 @@ final class Buffer with _BufferMixin { /// Writes an unsigned little-endian 64-bit integer. void writeUint64(Uint64 value) { ensureWritable(8); - _view.setUint32(_writerIndex, value.low32, Endian.little); - _view.setUint32(_writerIndex + 4, value.high32Unsigned, Endian.little); + _view.setInt64(_writerIndex, value.value, Endian.little); _writerIndex += 8; } /// Reads an unsigned little-endian 64-bit integer. Uint64 readUint64() { - final value = Uint64.fromWords( - _view.getUint32(_readerIndex, Endian.little), - _view.getUint32(_readerIndex + 4, Endian.little), - ); + final value = Uint64(_view.getInt64(_readerIndex, Endian.little)); _readerIndex += 8; return value; } @@ -102,14 +98,7 @@ final class Buffer with _BufferMixin { @override void writeVarUint64(Uint64 value) { ensureWritable(10); - var remaining = value; - for (var shift = 0; shift < 56 && remaining > 0x7f; shift += 7) { - _bytes[_writerIndex] = (remaining.low32 & 0x7f) | 0x80; - _writerIndex += 1; - remaining = remaining >> 7; - } - _bytes[_writerIndex] = remaining.toInt(); - _writerIndex += 1; + _writeVarUint64Int(value.value); } /// Reads an unsigned 64-bit varint. @@ -133,7 +122,8 @@ final class Buffer with _BufferMixin { /// Writes a zig-zag encoded signed 64-bit varint. void writeVarInt64(Int64 value) { - writeVarUint64(Uint64((value << 1) ^ (value >> 63))); + ensureWritable(10); + _writeVarUint64Int((value << 1) ^ (value >> 63)); } /// Writes a zig-zag encoded signed 64-bit varint from a Dart [int]. @@ -221,10 +211,7 @@ final class Buffer with _BufferMixin { _readerIndex = readIndex + 4; return Uint64(first >>> 1); } - final value = Uint64.fromWords( - _view.getUint32(readIndex + 1, Endian.little), - _view.getUint32(readIndex + 5, Endian.little), - ); + final value = Uint64(_view.getInt64(readIndex + 1, Endian.little)); _readerIndex = readIndex + 9; return value; } diff --git a/dart/packages/fory/lib/src/resolver/type_resolver.dart b/dart/packages/fory/lib/src/resolver/type_resolver.dart index 20f8e5925f..8e826275f0 100644 --- a/dart/packages/fory/lib/src/resolver/type_resolver.dart +++ b/dart/packages/fory/lib/src/resolver/type_resolver.dart @@ -324,11 +324,12 @@ final class TypeResolver { if (value is Int32) { return _builtin(Int32, TypeIds.varInt32); } - // The native extension type is represented as `int`, but web needs this - // branch to keep explicit Int64 wrapper values distinct from plain ints. + // Native Int64 is represented as `int`; web still needs this branch to + // keep explicit wrapper values on the Int64 API while using varint64 wire + // format by default, the same as plain Dart int. // ignore: unnecessary_type_check if (value is Int64 && value is! int) { - return _builtin(Int64, TypeIds.int64); + return _builtin(Int64, TypeIds.varInt64); } if (value is int) { return _builtin(int, TypeIds.varInt64); @@ -340,10 +341,10 @@ final class TypeResolver { return _builtin(Uint16, TypeIds.uint16); } if (value is Uint32) { - return _builtin(Uint32, TypeIds.uint32); + return _builtin(Uint32, TypeIds.varUint32); } if (value is Uint64) { - return _builtin(Uint64, TypeIds.uint64); + return _builtin(Uint64, TypeIds.varUint64); } if (value is Float16) { return _builtin(Float16, TypeIds.float16); @@ -1115,6 +1116,14 @@ final class TypeResolver { } } + TypeInfo resolveExpectedRootWireType(TypeInfo resolved) { + if ((_isType() || _isType()) && + resolved.typeId == TypeIds.varInt64) { + return _builtin(Int64, TypeIds.varInt64); + } + return resolved; + } + TypeInfo _builtin(Type type, int typeId) { final key = _BuiltinKey(type, typeId); final cached = _builtinByKey[key]; @@ -1269,10 +1278,10 @@ final class TypeResolver { return TypeIds.uint16; } if (type == Uint32) { - return TypeIds.uint32; + return TypeIds.varUint32; } if (type == Uint64) { - return TypeIds.uint64; + return TypeIds.varUint64; } if (type == Uint64List) { return TypeIds.uint64Array; @@ -1499,6 +1508,8 @@ final class TypeResolver { } } +bool _isType() => T == U; + final class _BuiltinKey { final Type type; final int typeId; diff --git a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart index 4d671d78a3..af8e249ee8 100644 --- a/dart/packages/fory/lib/src/serializer/primitive_serializers.dart +++ b/dart/packages/fory/lib/src/serializer/primitive_serializers.dart @@ -33,6 +33,9 @@ import 'package:fory/src/types/uint32.dart'; import 'package:fory/src/types/uint64.dart'; import 'package:fory/src/types/uint8.dart'; +const int _jsSafeUint64IntMax = 9007199254740991; +const bool _isWeb = bool.fromEnvironment('dart.library.js_interop'); + final class PrimitiveSerializer extends Serializer { final int typeId; final bool _supportsRef; @@ -78,13 +81,25 @@ final class PrimitiveSerializer extends Serializer { buffer.writeVarInt32(value is Int32 ? value.value : value as int); return; case TypeIds.int64: - buffer.writeInt64(value is Int64 ? value : Int64(value as int)); + if (value is Int64) { + buffer.writeInt64(value); + } else { + buffer.writeInt64FromInt(value as int); + } return; case TypeIds.varInt64: - buffer.writeVarInt64(value is Int64 ? value : Int64(value as int)); + if (value is Int64) { + buffer.writeVarInt64(value); + } else { + buffer.writeVarInt64FromInt(value as int); + } return; case TypeIds.taggedInt64: - buffer.writeTaggedInt64(value is Int64 ? value : Int64(value as int)); + if (value is Int64) { + buffer.writeTaggedInt64(value); + } else { + buffer.writeTaggedInt64FromInt(value as int); + } return; case TypeIds.uint8: buffer.writeUint8(value is Uint8 ? value.value : value as int); @@ -99,15 +114,13 @@ final class PrimitiveSerializer extends Serializer { buffer.writeVarUint32(value is Uint32 ? value.value : value as int); return; case TypeIds.uint64: - buffer.writeUint64(value is Uint64 ? value : Uint64(value as int)); + buffer.writeUint64(_uint64Value(value)); return; case TypeIds.varUint64: - buffer.writeVarUint64(value is Uint64 ? value : Uint64(value as int)); + buffer.writeVarUint64(_uint64Value(value)); return; case TypeIds.taggedUint64: - buffer.writeTaggedUint64( - value is Uint64 ? value : Uint64(value as int), - ); + buffer.writeTaggedUint64(_uint64Value(value)); return; case TypeIds.float16: buffer.writeFloat16(value as Float16); @@ -176,6 +189,21 @@ final class PrimitiveSerializer extends Serializer { } } +Uint64 _uint64Value(Object value) { + if (value is Uint64) { + return value; + } + final intValue = value as int; + if (_isWeb && (intValue < 0 || intValue > _jsSafeUint64IntMax)) { + throw StateError( + 'Dart int value $intValue is outside the JS-safe unsigned uint64 ' + 'int field range [0, $_jsSafeUint64IntMax]. Use Uint64 for full ' + 'unsigned 64-bit values on web.', + ); + } + return Uint64(intValue); +} + const PrimitiveSerializer boolSerializer = PrimitiveSerializer( TypeIds.boolType, supportsRef: false, @@ -201,7 +229,8 @@ const PrimitiveSerializer int64Serializer = PrimitiveSerializer( TypeIds.int64, supportsRef: false, ); -const PrimitiveSerializer varInt64Serializer = PrimitiveSerializer( +const PrimitiveSerializer varInt64Serializer = + PrimitiveSerializer( TypeIds.varInt64, supportsRef: false, ); diff --git a/dart/packages/fory/lib/src/types/int64_web.dart b/dart/packages/fory/lib/src/types/int64_web.dart index 417cc68f3e..ecb53c9934 100644 --- a/dart/packages/fory/lib/src/types/int64_web.dart +++ b/dart/packages/fory/lib/src/types/int64_web.dart @@ -443,12 +443,21 @@ final class Int64List extends IterableBase { /// Creates a zero-copy element-range view of [data]. factory Int64List.sublistView(td.TypedData data, [int start = 0, int? end]) { - final totalLength = data.lengthInBytes ~/ bytesPerElement; - final endIndex = RangeError.checkValidRange(start, end, totalLength); + final elementCount = data.lengthInBytes ~/ data.elementSizeInBytes; + final endIndex = RangeError.checkValidRange(start, end, elementCount); + final byteOffset = data.offsetInBytes + start * data.elementSizeInBytes; + final byteLength = (endIndex - start) * data.elementSizeInBytes; + if (byteOffset % bytesPerElement != 0 || + byteLength % bytesPerElement != 0) { + throw ArgumentError( + 'The selected byte range must align to $bytesPerElement-byte ' + 'Int64 elements.', + ); + } return Int64List.view( data.buffer, - data.offsetInBytes + start * bytesPerElement, - endIndex - start, + byteOffset, + byteLength ~/ bytesPerElement, ); } diff --git a/dart/packages/fory/lib/src/types/uint64_web.dart b/dart/packages/fory/lib/src/types/uint64_web.dart index 0397d2cac4..b15d7d27b9 100644 --- a/dart/packages/fory/lib/src/types/uint64_web.dart +++ b/dart/packages/fory/lib/src/types/uint64_web.dart @@ -375,12 +375,21 @@ final class Uint64List extends IterableBase { /// Creates a zero-copy element-range view of [data]. factory Uint64List.sublistView(td.TypedData data, [int start = 0, int? end]) { - final totalLength = data.lengthInBytes ~/ bytesPerElement; - final endIndex = RangeError.checkValidRange(start, end, totalLength); + final elementCount = data.lengthInBytes ~/ data.elementSizeInBytes; + final endIndex = RangeError.checkValidRange(start, end, elementCount); + final byteOffset = data.offsetInBytes + start * data.elementSizeInBytes; + final byteLength = (endIndex - start) * data.elementSizeInBytes; + if (byteOffset % bytesPerElement != 0 || + byteLength % bytesPerElement != 0) { + throw ArgumentError( + 'The selected byte range must align to $bytesPerElement-byte ' + 'Uint64 elements.', + ); + } return Uint64List.view( data.buffer, - data.offsetInBytes + start * bytesPerElement, - endIndex - start, + byteOffset, + byteLength ~/ bytesPerElement, ); } diff --git a/dart/packages/fory/test/buffer_test.dart b/dart/packages/fory/test/buffer_test.dart index 3eef20b06c..2a5ddb1f70 100644 --- a/dart/packages/fory/test/buffer_test.dart +++ b/dart/packages/fory/test/buffer_test.dart @@ -91,6 +91,16 @@ void main() { expect(buffer.toBytes().last, equals(7)); }); + test('grows from zero-capacity storage', () { + final buffer = Buffer(0); + buffer.writeBytes([1, 2, 3]); + expect(buffer.toBytes(), orderedEquals([1, 2, 3])); + + final wrapped = Buffer.wrap(Uint8List(0)); + wrapped.writeUint8(4); + expect(wrapped.toBytes(), orderedEquals([4])); + }); + test( 'wrap constructor, wrap method, read views, copyBytes, skip, and clear manage indices', () { @@ -378,6 +388,40 @@ void main() { } }); + test( + 'uint64 cursor int helpers match Uint64 wrapper encodings at safe boundaries', + () { + const cases = [ + 0, + 1, + 0x7fffffff, + 0x80000000, + 0xffffffff, + _jsSafeIntMax, + ]; + + for (final value in cases) { + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => cursor.writeUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => cursor.writeUint64(value), + ); + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => + cursor.writeVarUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => cursor.writeVarUint64(value), + ); + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => + cursor.writeTaggedUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => + cursor.writeTaggedUint64(value), + ); + } + }); + test('web rejects JS-unsafe int64 int helper values', () { if (!identical(1, 1.0)) { final buffer = Buffer(); @@ -422,6 +466,53 @@ void main() { ); }); + test('web rejects unsafe uint64 cursor int helper values', () { + if (!identical(1, 1.0)) { + for (final value in [-1, _jsUnsafeInt]) { + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => cursor.writeUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => cursor.writeUint64(value), + ); + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => + cursor.writeVarUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => cursor.writeVarUint64(value), + ); + _expectUint64CursorIntHelperMatchesWrapper( + value: value, + cursorWriteInt: (cursor, value) => + cursor.writeTaggedUint64FromInt(value), + cursorWriteWrapper: (cursor, value) => + cursor.writeTaggedUint64(value), + ); + } + return; + } + + for (final value in [-1, _jsUnsafeInt]) { + expect( + () => _writeWithCursor((cursor) => cursor.writeUint64FromInt(value)), + throwsA(isA()), + reason: 'fixed $value', + ); + expect( + () => + _writeWithCursor((cursor) => cursor.writeVarUint64FromInt(value)), + throwsA(isA()), + reason: 'varint $value', + ); + expect( + () => _writeWithCursor( + (cursor) => cursor.writeTaggedUint64FromInt(value), + ), + throwsA(isA()), + reason: 'tagged $value', + ); + } + }); + test('round-trips small varuint helpers', () { const small7Cases = <({int bytes, int value})>[ (bytes: 1, value: 0), @@ -587,6 +678,32 @@ void _expectInt64IntHelperMatchesWrapper({ expect(cursorReadBuffer.readableBytes, equals(0)); } +void _expectUint64CursorIntHelperMatchesWrapper({ + required int value, + required void Function(GeneratedWriteCursor cursor, int value) cursorWriteInt, + required void Function(GeneratedWriteCursor cursor, Uint64 value) + cursorWriteWrapper, +}) { + final intBuffer = Buffer(); + final intCursor = GeneratedWriteCursor.reserve(intBuffer, 10); + cursorWriteInt(intCursor, value); + intCursor.finish(); + + final wrapperBuffer = Buffer(); + final wrapperCursor = GeneratedWriteCursor.reserve(wrapperBuffer, 10); + cursorWriteWrapper(wrapperCursor, Uint64(value)); + wrapperCursor.finish(); + + expect(intBuffer.toBytes(), orderedEquals(wrapperBuffer.toBytes())); +} + +void _writeWithCursor(void Function(GeneratedWriteCursor cursor) write) { + final buffer = Buffer(); + final cursor = GeneratedWriteCursor.reserve(buffer, 10); + write(cursor); + cursor.finish(); +} + void _expectEncodedIntRoundTrip({ required int value, required int expectedBytes, diff --git a/dart/packages/fory/test/collection_serializer_test.dart b/dart/packages/fory/test/collection_serializer_test.dart index 4c6fb77390..573bc76b9a 100644 --- a/dart/packages/fory/test/collection_serializer_test.dart +++ b/dart/packages/fory/test/collection_serializer_test.dart @@ -22,6 +22,58 @@ import 'dart:convert'; import 'package:fory/fory.dart'; import 'package:test/test.dart'; +part 'collection_serializer_test.fory.dart'; + +final Int64 _int64Min = Int64.parseHex('8000000000000000'); +final Int64 _int64Max = Int64.parseHex('7fffffffffffffff'); +final Uint64 _uint64HighBit = Uint64.parseHex('8000000000000000'); +final Uint64 _uint64Max = Uint64.parseHex('ffffffffffffffff'); + +@ForyStruct() +class NumericContainerEnvelope { + NumericContainerEnvelope(); + + List int64s = []; + List uint64s = []; + Map int64ByName = {}; + Map uint64ByName = {}; +} + +void _registerNumericContainerEnvelope(Fory fory) { + CollectionSerializerTestFory.register( + fory, + NumericContainerEnvelope, + namespace: 'test', + typeName: 'NumericContainerEnvelope', + ); +} + +NumericContainerEnvelope _numericContainerEnvelope() { + return NumericContainerEnvelope() + ..int64s = [Int64(-1), Int64(0), _int64Min, _int64Max] + ..uint64s = [Uint64(0), Uint64(1), _uint64HighBit, _uint64Max] + ..int64ByName = { + 'negative': Int64(-1), + 'min': _int64Min, + 'max': _int64Max, + } + ..uint64ByName = { + 'zero': Uint64(0), + 'highBit': _uint64HighBit, + 'max': _uint64Max, + }; +} + +void _expectNumericContainerEqual( + NumericContainerEnvelope actual, + NumericContainerEnvelope expected, +) { + expect(actual.int64s, equals(expected.int64s)); + expect(actual.uint64s, equals(expected.uint64s)); + expect(actual.int64ByName, equals(expected.int64ByName)); + expect(actual.uint64ByName, equals(expected.uint64ByName)); +} + void main() { group('collection serializer', () { test('round-trips empty root containers', () { @@ -262,6 +314,19 @@ void main() { } }); + test('round-trips generated Int64 and Uint64 list/map fields', () { + for (final compatible in [false, true]) { + final fory = Fory(compatible: compatible); + _registerNumericContainerEnvelope(fory); + + final value = _numericContainerEnvelope(); + final roundTrip = fory.deserialize( + fory.serialize(value), + ); + _expectNumericContainerEqual(roundTrip, value); + } + }); + test('enforces maxCollectionSize for list set and map', () { final fory = Fory(maxCollectionSize: 2); diff --git a/dart/packages/fory/test/int64_uint64_arithmetic_test.dart b/dart/packages/fory/test/int64_uint64_arithmetic_test.dart index 9224a0844e..c75e00e228 100644 --- a/dart/packages/fory/test/int64_uint64_arithmetic_test.dart +++ b/dart/packages/fory/test/int64_uint64_arithmetic_test.dart @@ -17,6 +17,8 @@ * under the License. */ +import 'dart:typed_data'; + import 'package:fory/fory.dart'; import 'package:test/test.dart'; @@ -320,6 +322,29 @@ void main() { reason: 'unsafe negative boundary', ); }); + + test('list sublistView respects source typed-data element ranges', () { + final bytes = Uint8List(24); + final data = ByteData.sublistView(bytes); + data.setUint32(8, 0x76543210, Endian.little); + data.setUint32(12, 0x01234567, Endian.little); + + final byteView = Int64List.sublistView(bytes, 8, 16); + expect(byteView.length, equals(1)); + expect(byteView[0], equals(Int64.fromWords(0x76543210, 0x01234567))); + + byteView[0] = Int64.fromWords(0x89abcdef, 0x76543210); + expect(data.getUint32(8, Endian.little), equals(0x89abcdef)); + expect(data.getUint32(12, Endian.little), equals(0x76543210)); + + final wordView = + Int64List.sublistView(Uint32List.view(bytes.buffer), 2, 4); + expect(wordView.length, equals(1)); + expect(wordView[0], equals(Int64.fromWords(0x89abcdef, 0x76543210))); + + expect(() => Int64List.sublistView(bytes, 1, 9), throwsArgumentError); + expect(() => Int64List.sublistView(bytes, 8, 15), throwsArgumentError); + }); }); group('Uint64', () { @@ -531,5 +556,28 @@ void main() { reason: 'max boundary', ); }); + + test('list sublistView respects source typed-data element ranges', () { + final bytes = Uint8List(24); + final data = ByteData.sublistView(bytes); + data.setUint32(8, 0xfedcba98, Endian.little); + data.setUint32(12, 0x01234567, Endian.little); + + final byteView = Uint64List.sublistView(bytes, 8, 16); + expect(byteView.length, equals(1)); + expect(byteView[0], equals(Uint64.fromWords(0xfedcba98, 0x01234567))); + + byteView[0] = Uint64.fromWords(0x76543210, 0xfedcba98); + expect(data.getUint32(8, Endian.little), equals(0x76543210)); + expect(data.getUint32(12, Endian.little), equals(0xfedcba98)); + + final wordView = + Uint64List.sublistView(Uint32List.view(bytes.buffer), 2, 4); + expect(wordView.length, equals(1)); + expect(wordView[0], equals(Uint64.fromWords(0x76543210, 0xfedcba98))); + + expect(() => Uint64List.sublistView(bytes, 1, 9), throwsArgumentError); + expect(() => Uint64List.sublistView(bytes, 8, 15), throwsArgumentError); + }); }); } diff --git a/dart/packages/fory/test/numeric_wrapper_test.dart b/dart/packages/fory/test/numeric_wrapper_test.dart index 0945940e2d..969919cdf9 100644 --- a/dart/packages/fory/test/numeric_wrapper_test.dart +++ b/dart/packages/fory/test/numeric_wrapper_test.dart @@ -20,6 +20,8 @@ import 'dart:typed_data'; import 'package:fory/fory.dart'; +import 'package:fory/src/resolver/type_resolver.dart'; +import 'package:fory/src/serializer/compatible_struct_metadata.dart'; import 'package:test/test.dart'; part 'numeric_wrapper_test.fory.dart'; @@ -40,6 +42,7 @@ class NumericWrappersEnvelope { Int8 i8 = Int8(0); Int16 i16 = Int16(0); Int32 i32 = Int32(0); + Int64 i64 = Int64(0); Uint8 u8 = Uint8(0); Uint16 u16 = Uint16(0); Uint32 u32 = Uint32(0); @@ -54,6 +57,13 @@ class NumericWrappersEnvelope { Float32? optionalSingle; } +@ForyStruct() +class NumericWrappersMetadataReader { + NumericWrappersMetadataReader(); + + Int8 i8 = Int8(0); +} + void _registerNumericWrappers(Fory fory) { NumericWrapperTestFory.register( fory, @@ -63,6 +73,15 @@ void _registerNumericWrappers(Fory fory) { ); } +void _registerNumericWrappersMetadataReader(Fory fory) { + NumericWrapperTestFory.register( + fory, + NumericWrappersMetadataReader, + namespace: 'test', + typeName: 'NumericWrappersEnvelope', + ); +} + T _roundTrip(Fory fory, T value) => fory.deserialize(fory.serialize(value)); @@ -71,6 +90,7 @@ NumericWrappersEnvelope _sampleEnvelope() { ..i8 = Int8(-127) ..i16 = Int16(0x7fff) ..i32 = Int32(-2147483648) + ..i64 = Int64.parseHex('8000000000000000') ..u8 = Uint8(0xff) ..u16 = Uint16(0xffff) ..u32 = Uint32(0xffffffff) @@ -92,6 +112,7 @@ void _expectEnvelopeEquals( expect(actual.i8, equals(expected.i8)); expect(actual.i16, equals(expected.i16)); expect(actual.i32, equals(expected.i32)); + expect(actual.i64, equals(expected.i64)); expect(actual.u8, equals(expected.u8)); expect(actual.u16, equals(expected.u16)); expect(actual.u32, equals(expected.u32)); @@ -111,6 +132,15 @@ void _expectEnvelopeEquals( ); } +int _remoteFieldTypeId(Object value, String identifier) { + final remoteTypeDef = CompatibleStructMetadata.remoteTypeDefFor(value); + expect(remoteTypeDef, isNotNull); + final field = remoteTypeDef!.fields.firstWhere( + (field) => field.identifier == identifier, + ); + return field.fieldType.typeId; +} + void main() { group('numeric wrappers', () { test('signed integer wrappers normalize arithmetic and bitwise operations', @@ -228,12 +258,106 @@ void main() { expect(_roundTrip(fory, Uint8(-1)), equals(Uint8(0xff))); expect(_roundTrip(fory, Uint16(-1)), equals(Uint16(0xffff))); expect(_roundTrip(fory, Uint32(-1)), equals(Uint32(0xffffffff))); + expect(_roundTrip(fory, Int64(-1)), equals(Int64(-1))); + expect( + _roundTrip(fory, Int64.parseHex('8000000000000000')), + equals(Int64.parseHex('8000000000000000')), + ); + expect( + _roundTrip(fory, Int64.parseHex('7fffffffffffffff')), + equals(Int64.parseHex('7fffffffffffffff')), + ); expect( _roundTrip(fory, Uint64(-1)), equals(_u64Hex('ffffffffffffffff')), ); }); + test('resolves root numeric wrappers to compact varint wire ids', () { + final resolver = TypeResolver(Config()); + + expect(resolver.resolveValue(1).typeId, equals(TypeIds.varInt64)); + expect(resolver.resolveValue(Int32(1)).typeId, equals(TypeIds.varInt32)); + expect(resolver.resolveValue(Int64(1)).typeId, equals(TypeIds.varInt64)); + expect( + resolver.resolveValue(Uint32(1)).typeId, + equals(TypeIds.varUint32), + ); + if (identical(1, 1.0)) { + expect( + resolver.resolveValue(Uint64(1)).typeId, + equals(TypeIds.varUint64), + ); + } else { + expect( + resolver.resolveValue(Uint64(1)).typeId, + equals(TypeIds.varInt64), + reason: 'Native Uint64 is an extension type represented as int.', + ); + } + }); + + test('typed root Int64 reads preserve wrappers', () { + final fory = Fory(); + + expect( + fory.deserialize(fory.serialize(1)), + equals(1), + reason: 'Plain int roots still decode as Dart int.', + ); + expect( + fory.deserialize(fory.serialize(Int64(-1))), + equals(Int64(-1)), + ); + expect( + fory.deserialize( + fory.serialize(Int64.parseHex('8000000000000000')), + ), + equals(Int64.parseHex('8000000000000000')), + ); + expect( + fory.deserialize( + fory.serialize(Int64.parseHex('7fffffffffffffff')), + ), + equals(Int64.parseHex('7fffffffffffffff')), + ); + expect( + fory.deserialize( + fory.serialize(Int64.parseHex('8000000000000000')), + ), + equals(Int64.parseHex('8000000000000000')), + ); + expect( + fory.deserialize(fory.serialize(Int64(-1))), + equals(-1), + reason: 'Nullable int roots still decode as Dart int.', + ); + }); + + test('web dynamic Uint64 wrappers keep unsigned metadata', () { + if (!identical(1, 1.0)) { + return; + } + + final fory = Fory(); + final value = _u64Hex('ffffffffffffffff'); + + expect( + fory.deserialize(fory.serialize(value)), + equals(value), + ); + + final list = fory.deserialize( + fory.serialize([value]), + ) as List; + expect(list.single, equals(value)); + + final map = fory.deserialize( + fory.serialize({'value': value}), + ) as Map; + expect(map['value'], equals(value)); + }); + test('round-trips root Float16 payloads with exact bits', () { final fory = Fory(); final cases = [ @@ -308,6 +432,23 @@ void main() { _expectEnvelopeEquals(roundTrip, value); }); + test('compatible metadata records numeric wrapper varint defaults', () { + final writer = Fory(compatible: true); + final reader = Fory(compatible: true); + _registerNumericWrappers(writer); + _registerNumericWrappersMetadataReader(reader); + + final roundTrip = reader.deserialize( + writer.serialize(_sampleEnvelope()), + ); + + expect(roundTrip.i8, equals(Int8(-127))); + expect(_remoteFieldTypeId(roundTrip, 'i32'), equals(TypeIds.varInt32)); + expect(_remoteFieldTypeId(roundTrip, 'i64'), equals(TypeIds.varInt64)); + expect(_remoteFieldTypeId(roundTrip, 'u32'), equals(TypeIds.varUint32)); + expect(_remoteFieldTypeId(roundTrip, 'u64'), equals(TypeIds.varUint64)); + }); + test('supports null optional numeric wrapper fields', () { final fory = Fory(); _registerNumericWrappers(fory); diff --git a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart index b67b6dbc17..7f7d4013c1 100644 --- a/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart +++ b/dart/packages/fory/test/scalar_and_typed_array_serializer_test.dart @@ -27,6 +27,11 @@ part 'scalar_and_typed_array_serializer_test.fory.dart'; Timestamp _timestamp(int seconds, int nanoseconds) => Timestamp(Int64(seconds), nanoseconds); +final Int64 _int64Min = Int64.parseHex('8000000000000000'); +final Int64 _int64Max = Int64.parseHex('7fffffffffffffff'); +final Uint64 _uint64HighBit = Uint64.parseHex('8000000000000000'); +final Uint64 _uint64Max = Uint64.parseHex('ffffffffffffffff'); + @ForyStruct() class ScalarAndArrayEnvelope { ScalarAndArrayEnvelope(); @@ -70,10 +75,13 @@ ScalarAndArrayEnvelope _sampleEnvelope() { ..int8s = Int8List.fromList([-128, -1, 0, 127]) ..int16s = Int16List.fromList([-32768, -1, 0, 32767]) ..int32s = Int32List.fromList([-1, 0, 1, 123456789]) - ..int64s = Int64List.fromList([-1, 0, 1, 1 << 40]) + ..int64s = + Int64List.fromList([-1, 0, 1, 1 << 40, _int64Min, _int64Max]) ..uint16s = Uint16List.fromList([0, 1, 65535]) ..uint32s = Uint32List.fromList([0, 1, 0x7fffffff]) - ..uint64s = Uint64List.fromList([0, 1, 1 << 40]) + ..uint64s = Uint64List.fromList( + [0, 1, 1 << 40, _uint64HighBit, _uint64Max], + ) ..float16s = Float16List.fromList([ Float16.fromBits(0x8000), Float16.fromBits(0x3555), @@ -258,9 +266,23 @@ void main() { _expectInt64ListEquals( _roundTripRoot( fory, - Int64List.fromList([-1, 0, 1, 1 << 40]), + Int64List.fromList([ + -1, + 0, + 1, + 1 << 40, + _int64Min, + _int64Max, + ]), ), - Int64List.fromList([-1, 0, 1, 1 << 40]), + Int64List.fromList([ + -1, + 0, + 1, + 1 << 40, + _int64Min, + _int64Max, + ]), ); _expectUint16ListEquals( _roundTripRoot( @@ -279,9 +301,21 @@ void main() { _expectUint64ListEquals( _roundTripRoot( fory, - Uint64List.fromList([0, 1, 1 << 40]), + Uint64List.fromList([ + 0, + 1, + 1 << 40, + _uint64HighBit, + _uint64Max, + ]), ), - Uint64List.fromList([0, 1, 1 << 40]), + Uint64List.fromList([ + 0, + 1, + 1 << 40, + _uint64HighBit, + _uint64Max, + ]), ); _expectFloat16ListEquals( _roundTripRoot( diff --git a/dart/packages/fory/test/signed_serializer_test.dart b/dart/packages/fory/test/signed_serializer_test.dart index 182e6c676d..176854f368 100644 --- a/dart/packages/fory/test/signed_serializer_test.dart +++ b/dart/packages/fory/test/signed_serializer_test.dart @@ -35,6 +35,12 @@ class SignedFields { int plainInt = 0; + @Int32Type(compress: true) + int i32Var = 0; + + @Int32Type(compress: false) + int i32Fixed = 0; + @Int64Type(encoding: LongEncoding.varint) int i64VarInt = 0; @@ -58,6 +64,12 @@ class SignedFields { @Int64Type(encoding: LongEncoding.varint) int? optionalI64VarInt; + @Int32Type(compress: true) + int? optionalI32Var; + + @Int32Type(compress: false) + int? optionalI32Fixed; + @Int64Type(encoding: LongEncoding.fixed) Int64? optionalI64Fixed; @@ -72,6 +84,20 @@ class SignedMetadataReader { int plainInt = 0; } +@ForyStruct() +class SignedIntFieldsReader { + SignedIntFieldsReader(); + + @Int64Type(encoding: LongEncoding.varint) + int i64Var = 0; + + @Int64Type(encoding: LongEncoding.fixed) + int i64Fixed = 0; + + @Int64Type(encoding: LongEncoding.tagged) + int i64Tagged = 0; +} + void _registerSignedFields(Fory fory) { SignedSerializerTestFory.register( fory, @@ -90,9 +116,20 @@ void _registerSignedMetadataReader(Fory fory) { ); } +void _registerSignedIntFieldsReader(Fory fory) { + SignedSerializerTestFory.register( + fory, + SignedIntFieldsReader, + namespace: 'test', + typeName: 'SignedFields', + ); +} + SignedFields _smallSignedFields() { return SignedFields() ..plainInt = -1 + ..i32Var = -64 + ..i32Fixed = 63 ..i64VarInt = -64 ..i64FixedInt = 63 ..i64TaggedInt = 0x3fffffff @@ -100,6 +137,8 @@ SignedFields _smallSignedFields() { ..i64Var = Int64(1) ..i64Fixed = Int64(-0x40000000) ..i64Tagged = Int64(0x3fffffff) + ..optionalI32Var = -128 + ..optionalI32Fixed = 0x40000000 ..optionalI64VarInt = -128 ..optionalI64Fixed = Int64(0x40000000) ..optionalI64Tagged = Int64(-0x40000001); @@ -108,6 +147,8 @@ SignedFields _smallSignedFields() { SignedFields _jsSafeBoundarySignedFields() { return SignedFields() ..plainInt = _jsSafeIntMin + ..i32Var = -0x80000000 + ..i32Fixed = 0x7fffffff ..i64VarInt = _jsSafeIntMax ..i64FixedInt = _jsSafeIntMin ..i64TaggedInt = _jsSafeIntMax @@ -115,6 +156,8 @@ SignedFields _jsSafeBoundarySignedFields() { ..i64Var = Int64(_jsSafeIntMax) ..i64Fixed = Int64(_jsSafeIntMin) ..i64Tagged = Int64(_jsSafeIntMax) + ..optionalI32Var = -0x80000000 + ..optionalI32Fixed = 0x7fffffff ..optionalI64VarInt = _jsSafeIntMin ..optionalI64Fixed = Int64(_jsSafeIntMax) ..optionalI64Tagged = Int64(_jsSafeIntMin); @@ -123,6 +166,8 @@ SignedFields _jsSafeBoundarySignedFields() { SignedFields _fullRangeWrapperSignedFields() { return SignedFields() ..plainInt = 0 + ..i32Var = -0x80000000 + ..i32Fixed = 0x7fffffff ..i64VarInt = -0x40000001 ..i64FixedInt = 0x40000000 ..i64TaggedInt = -0x40000001 @@ -130,6 +175,8 @@ SignedFields _fullRangeWrapperSignedFields() { ..i64Var = _int64Max ..i64Fixed = _int64Min ..i64Tagged = _int64Max + ..optionalI32Var = -0x80000000 + ..optionalI32Fixed = 0x7fffffff ..optionalI64VarInt = 0x40000000 ..optionalI64Fixed = _int64Max ..optionalI64Tagged = _int64Min; @@ -138,6 +185,8 @@ SignedFields _fullRangeWrapperSignedFields() { SignedFields _nullSignedFields() { return SignedFields() ..plainInt = 1 + ..i32Var = 2 + ..i32Fixed = 3 ..i64VarInt = 2 ..i64FixedInt = 3 ..i64TaggedInt = 4 @@ -145,13 +194,35 @@ SignedFields _nullSignedFields() { ..i64Var = Int64(6) ..i64Fixed = Int64(7) ..i64Tagged = Int64(8) + ..optionalI32Var = null + ..optionalI32Fixed = null ..optionalI64VarInt = null ..optionalI64Fixed = null ..optionalI64Tagged = null; } +SignedFields _int64ReadMismatchPayload(String encoding) { + final value = _smallSignedFields() + ..i64Var = Int64(1) + ..i64Fixed = Int64(1) + ..i64Tagged = Int64(1); + switch (encoding) { + case 'varint': + value.i64Var = _int64Min; + case 'fixed': + value.i64Fixed = _int64Min; + case 'tagged': + value.i64Tagged = _int64Min; + default: + throw ArgumentError.value(encoding, 'encoding'); + } + return value; +} + void _expectSignedFieldsEqual(SignedFields actual, SignedFields expected) { expect(actual.plainInt, equals(expected.plainInt)); + expect(actual.i32Var, equals(expected.i32Var)); + expect(actual.i32Fixed, equals(expected.i32Fixed)); expect(actual.i64VarInt, equals(expected.i64VarInt)); expect(actual.i64FixedInt, equals(expected.i64FixedInt)); expect(actual.i64TaggedInt, equals(expected.i64TaggedInt)); @@ -159,6 +230,8 @@ void _expectSignedFieldsEqual(SignedFields actual, SignedFields expected) { expect(actual.i64Var, equals(expected.i64Var)); expect(actual.i64Fixed, equals(expected.i64Fixed)); expect(actual.i64Tagged, equals(expected.i64Tagged)); + expect(actual.optionalI32Var, equals(expected.optionalI32Var)); + expect(actual.optionalI32Fixed, equals(expected.optionalI32Fixed)); expect(actual.optionalI64VarInt, equals(expected.optionalI64VarInt)); expect(actual.optionalI64Fixed, equals(expected.optionalI64Fixed)); expect(actual.optionalI64Tagged, equals(expected.optionalI64Tagged)); @@ -199,6 +272,49 @@ void main() { } }); + test('compatible mode round trips Int32 and Int64 encoding edge cases', () { + final fory = Fory(compatible: true); + _registerSignedFields(fory); + + for (final value in [ + _smallSignedFields(), + _jsSafeBoundarySignedFields(), + _fullRangeWrapperSignedFields(), + _nullSignedFields(), + ]) { + final roundTrip = fory.deserialize(fory.serialize(value)); + _expectSignedFieldsEqual(roundTrip, value); + } + }); + + test('native schema-consistent Int64 varint int fields decode signed min', + () { + if (identical(1, 1.0)) { + return; + } + + final signedMin = _int64Min.toInt(); + final fory = Fory(); + _registerSignedFields(fory); + + final value = _smallSignedFields() + ..i64VarInt = signedMin + ..optionalI64VarInt = signedMin; + final roundTrip = fory.deserialize(fory.serialize(value)); + expect(roundTrip.i64VarInt, equals(signedMin)); + expect(roundTrip.optionalI64VarInt, equals(signedMin)); + + final buffer = Buffer(); + final writeCursor = GeneratedWriteCursor.reserve(buffer, 10); + writeCursor.writeVarInt64FromInt(signedMin); + writeCursor.finish(); + + final readCursor = + GeneratedReadCursor.start(Buffer.wrap(buffer.toBytes())); + expect(readCursor.readVarInt64AsInt(), equals(signedMin)); + readCursor.finish(); + }); + test('compatible metadata records signed wire types and nullability', () { final writer = Fory(compatible: true); final reader = Fory(compatible: true); @@ -214,6 +330,14 @@ void main() { _remoteFieldTypeId(roundTrip, 'plain_int'), equals(TypeIds.varInt64), ); + expect( + _remoteFieldTypeId(roundTrip, 'i32_var'), + equals(TypeIds.varInt32), + ); + expect( + _remoteFieldTypeId(roundTrip, 'i32_fixed'), + equals(TypeIds.int32), + ); expect( _remoteFieldTypeId(roundTrip, 'i64_var_int'), equals(TypeIds.varInt64), @@ -241,6 +365,8 @@ void main() { ); expect(_remoteFieldNullable(roundTrip, 'plain_int'), isFalse); + expect(_remoteFieldNullable(roundTrip, 'optional_i32_var'), isTrue); + expect(_remoteFieldNullable(roundTrip, 'optional_i32_fixed'), isTrue); expect(_remoteFieldNullable(roundTrip, 'optional_i64_var_int'), isTrue); expect(_remoteFieldNullable(roundTrip, 'optional_i64_fixed'), isTrue); expect(_remoteFieldNullable(roundTrip, 'optional_i64_tagged'), isTrue); @@ -248,15 +374,100 @@ void main() { test('web rejects JS-unsafe Dart int fields instead of corrupting bytes', () { + final cases = <({String name, SignedFields value})>[ + ( + name: 'plain int', + value: _smallSignedFields()..plainInt = _jsUnsafeInt, + ), + ( + name: 'varint', + value: _smallSignedFields()..i64VarInt = _jsUnsafeInt, + ), + ( + name: 'fixed', + value: _smallSignedFields()..i64FixedInt = _jsUnsafeInt, + ), + ( + name: 'tagged', + value: _smallSignedFields()..i64TaggedInt = _jsUnsafeInt, + ), + ( + name: 'nullable varint', + value: _smallSignedFields()..optionalI64VarInt = _jsUnsafeInt, + ), + ]; + + for (final testCase in cases) { + for (final compatible in [false, true]) { + final fory = Fory(compatible: compatible); + _registerSignedFields(fory); + if (identical(1, 1.0)) { + expect( + () => fory.serialize(testCase.value), + throwsA(isA()), + reason: '${testCase.name}, compatible=$compatible', + ); + } else { + final roundTrip = fory.deserialize( + fory.serialize(testCase.value), + ); + _expectSignedFieldsEqual(roundTrip, testCase.value); + } + } + } + }); + + test('web rejects JS-unsafe root and dynamic Dart ints', () { final fory = Fory(); - _registerSignedFields(fory); - final value = _smallSignedFields()..i64FixedInt = _jsUnsafeInt; if (identical(1, 1.0)) { - expect(() => fory.serialize(value), throwsA(isA())); + expect(() => fory.serialize(_jsUnsafeInt), throwsA(isA())); + expect( + () => fory.serialize([_jsUnsafeInt]), + throwsA(isA()), + ); } else { - final roundTrip = fory.deserialize(fory.serialize(value)); - _expectSignedFieldsEqual(roundTrip, value); + expect( + fory.deserialize(fory.serialize(_jsUnsafeInt)), + equals(_jsUnsafeInt), + ); + expect( + fory.deserialize>( + fory.serialize([_jsUnsafeInt]), + ), + equals([_jsUnsafeInt]), + ); + } + }); + + test('web rejects full-range Int64 wrapper payloads read as Dart int', () { + final writer = Fory(compatible: true); + final reader = Fory(compatible: true); + _registerSignedFields(writer); + _registerSignedIntFieldsReader(reader); + + for (final encoding in ['varint', 'fixed', 'tagged']) { + if (identical(1, 1.0)) { + expect( + () => reader.deserialize( + writer.serialize(_int64ReadMismatchPayload(encoding)), + ), + throwsA(isA()), + reason: encoding, + ); + } else { + final roundTrip = reader.deserialize( + writer.serialize(_int64ReadMismatchPayload(encoding)), + ); + switch (encoding) { + case 'varint': + expect(roundTrip.i64Var, equals(_int64Min.toInt())); + case 'fixed': + expect(roundTrip.i64Fixed, equals(_int64Min.toInt())); + case 'tagged': + expect(roundTrip.i64Tagged, equals(_int64Min.toInt())); + } + } } }); }); diff --git a/dart/packages/fory/test/unsigned_serializer_test.dart b/dart/packages/fory/test/unsigned_serializer_test.dart index 169c5602ca..6459f0f602 100644 --- a/dart/packages/fory/test/unsigned_serializer_test.dart +++ b/dart/packages/fory/test/unsigned_serializer_test.dart @@ -24,6 +24,8 @@ import 'package:test/test.dart'; part 'unsigned_serializer_test.fory.dart'; const int _uint32Midpoint = 0x80000000; +const int _jsSafeIntMax = 9007199254740991; +const int _jsUnsafeInt = 9007199254740992; final Uint64 _uint64Midpoint = Uint64.parseHex('8000000000000000'); final Uint64 _uint64Max = Uint64.parseHex('ffffffffffffffff'); @@ -52,6 +54,15 @@ class UnsignedFields { @Uint64Type(encoding: LongEncoding.tagged) Uint64 u64Tagged = Uint64(0); + @Uint64Type(encoding: LongEncoding.varint) + int u64VarInt = 0; + + @Uint64Type(encoding: LongEncoding.fixed) + int u64FixedInt = 0; + + @Uint64Type(encoding: LongEncoding.tagged) + int u64TaggedInt = 0; + @Uint8Type() int? u8Nullable; @@ -72,6 +83,9 @@ class UnsignedFields { @Uint64Type(encoding: LongEncoding.tagged) Uint64? u64TaggedNullable; + + @Uint64Type(encoding: LongEncoding.varint) + int? u64VarIntNullable; } @ForyStruct() @@ -84,6 +98,84 @@ class UnsignedMetadataReader { int extra = 42; } +@ForyStruct() +class UnsignedIntFieldsReader { + UnsignedIntFieldsReader(); + + @Uint64Type(encoding: LongEncoding.varint) + int u64Var = 0; + + @Uint64Type(encoding: LongEncoding.fixed) + int u64Fixed = 0; + + @Uint64Type(encoding: LongEncoding.tagged) + int u64Tagged = 0; +} + +@ForyStruct() +class UnsignedWrapperFields { + UnsignedWrapperFields(); + + @Uint64Type(encoding: LongEncoding.varint) + Uint64 u64Var = Uint64(0); + + @Uint64Type(encoding: LongEncoding.fixed) + Uint64 u64Fixed = Uint64(0); + + @Uint64Type(encoding: LongEncoding.tagged) + Uint64 u64Tagged = Uint64(0); +} + +@ForyStruct() +class UnsignedWrapperAsIntFields { + UnsignedWrapperAsIntFields(); + + @Uint64Type(encoding: LongEncoding.varint) + int u64Var = 0; + + @Uint64Type(encoding: LongEncoding.fixed) + int u64Fixed = 0; + + @Uint64Type(encoding: LongEncoding.tagged) + int u64Tagged = 0; +} + +UnsignedFields _uint64ReadMismatchPayload(String encoding) { + final value = _smallUnsignedFields() + ..u64Var = Uint64(1) + ..u64Fixed = Uint64(1) + ..u64Tagged = Uint64(1); + switch (encoding) { + case 'varint': + value.u64Var = _uint64Max; + case 'fixed': + value.u64Fixed = _uint64Max; + case 'tagged': + value.u64Tagged = _uint64Max; + default: + throw ArgumentError.value(encoding, 'encoding'); + } + return value; +} + +UnsignedWrapperFields _schemaUint64ReadMismatchPayload(String encoding) { + final value = UnsignedWrapperFields() + ..u64Var = Uint64(1) + ..u64Fixed = Uint64(1) + ..u64Tagged = Uint64(1); + switch (encoding) { + case 'varint': + value.u64Var = _uint64Max; + case 'fixed': + value.u64Fixed = _uint64Max; + case 'tagged': + value.u64Tagged = _uint64Max; + default: + throw ArgumentError.value(encoding, 'encoding'); + } + return value; +} + void _registerUnsignedFields(Fory fory) { UnsignedSerializerTestFory.register( fory, @@ -102,6 +194,33 @@ void _registerUnsignedMetadataReader(Fory fory) { ); } +void _registerUnsignedIntFieldsReader(Fory fory) { + UnsignedSerializerTestFory.register( + fory, + UnsignedIntFieldsReader, + namespace: 'test', + typeName: 'UnsignedFields', + ); +} + +void _registerUnsignedWrapperFields(Fory fory) { + UnsignedSerializerTestFory.register( + fory, + UnsignedWrapperFields, + namespace: 'test', + typeName: 'UnsignedSchemaUint64Fields', + ); +} + +void _registerUnsignedWrapperAsIntFields(Fory fory) { + UnsignedSerializerTestFory.register( + fory, + UnsignedWrapperAsIntFields, + namespace: 'test', + typeName: 'UnsignedSchemaUint64Fields', + ); +} + UnsignedFields _smallUnsignedFields() { return UnsignedFields() ..u8 = 0 @@ -111,13 +230,17 @@ UnsignedFields _smallUnsignedFields() { ..u64Var = Uint64(0) ..u64Fixed = Uint64(1) ..u64Tagged = Uint64(0) + ..u64VarInt = 0 + ..u64FixedInt = 1 + ..u64TaggedInt = 0 ..u8Nullable = 1 ..u16Nullable = 0 ..u32VarNullable = 1 ..u32FixedNullable = 0 ..u64VarNullable = Uint64(1) ..u64FixedNullable = Uint64(0) - ..u64TaggedNullable = Uint64(1); + ..u64TaggedNullable = Uint64(1) + ..u64VarIntNullable = 1; } UnsignedFields _taggedBoundaryUnsignedFields() { @@ -129,13 +252,17 @@ UnsignedFields _taggedBoundaryUnsignedFields() { ..u64Var = Uint64(0x7fffffff) ..u64Fixed = Uint64(0x80000000) ..u64Tagged = Uint64(0x7fffffff) + ..u64VarInt = 0x7fffffff + ..u64FixedInt = 0x80000000 + ..u64TaggedInt = 0x7fffffff ..u8Nullable = 0x80 ..u16Nullable = 0x8000 ..u32VarNullable = 0x80000000 ..u32FixedNullable = 0x7fffffff ..u64VarNullable = Uint64(0x80000000) ..u64FixedNullable = Uint64(0x7fffffff) - ..u64TaggedNullable = Uint64(0x80000000); + ..u64TaggedNullable = Uint64(0x80000000) + ..u64VarIntNullable = 0x80000000; } UnsignedFields _midpointUnsignedFields() { @@ -147,13 +274,17 @@ UnsignedFields _midpointUnsignedFields() { ..u64Var = _uint64Midpoint ..u64Fixed = _uint64Midpoint ..u64Tagged = _uint64Midpoint + ..u64VarInt = _jsSafeIntMax + ..u64FixedInt = _jsSafeIntMax + ..u64TaggedInt = _jsSafeIntMax ..u8Nullable = 0x80 ..u16Nullable = 0x8000 ..u32VarNullable = _uint32Midpoint ..u32FixedNullable = _uint32Midpoint ..u64VarNullable = _uint64Midpoint ..u64FixedNullable = _uint64Midpoint - ..u64TaggedNullable = _uint64Midpoint; + ..u64TaggedNullable = _uint64Midpoint + ..u64VarIntNullable = _jsSafeIntMax; } UnsignedFields _maxUnsignedFields() { @@ -165,13 +296,17 @@ UnsignedFields _maxUnsignedFields() { ..u64Var = _uint64Max ..u64Fixed = _uint64Max ..u64Tagged = _uint64Max + ..u64VarInt = _jsSafeIntMax + ..u64FixedInt = _jsSafeIntMax + ..u64TaggedInt = _jsSafeIntMax ..u8Nullable = 0xff ..u16Nullable = 0xffff ..u32VarNullable = 0xffffffff ..u32FixedNullable = 0xffffffff ..u64VarNullable = _uint64Max ..u64FixedNullable = _uint64Max - ..u64TaggedNullable = _uint64Max; + ..u64TaggedNullable = _uint64Max + ..u64VarIntNullable = _jsSafeIntMax; } UnsignedFields _nullUnsignedFields() { @@ -183,13 +318,17 @@ UnsignedFields _nullUnsignedFields() { ..u64Var = Uint64(5) ..u64Fixed = Uint64(6) ..u64Tagged = Uint64(7) + ..u64VarInt = 5 + ..u64FixedInt = 6 + ..u64TaggedInt = 7 ..u8Nullable = null ..u16Nullable = null ..u32VarNullable = null ..u32FixedNullable = null ..u64VarNullable = null ..u64FixedNullable = null - ..u64TaggedNullable = null; + ..u64TaggedNullable = null + ..u64VarIntNullable = null; } void _expectUnsignedFieldsEqual( @@ -201,6 +340,9 @@ void _expectUnsignedFieldsEqual( expect(actual.u64Var, equals(expected.u64Var)); expect(actual.u64Fixed, equals(expected.u64Fixed)); expect(actual.u64Tagged, equals(expected.u64Tagged)); + expect(actual.u64VarInt, equals(expected.u64VarInt)); + expect(actual.u64FixedInt, equals(expected.u64FixedInt)); + expect(actual.u64TaggedInt, equals(expected.u64TaggedInt)); expect(actual.u8Nullable, equals(expected.u8Nullable)); expect(actual.u16Nullable, equals(expected.u16Nullable)); expect(actual.u32VarNullable, equals(expected.u32VarNullable)); @@ -208,6 +350,7 @@ void _expectUnsignedFieldsEqual( expect(actual.u64VarNullable, equals(expected.u64VarNullable)); expect(actual.u64FixedNullable, equals(expected.u64FixedNullable)); expect(actual.u64TaggedNullable, equals(expected.u64TaggedNullable)); + expect(actual.u64VarIntNullable, equals(expected.u64VarIntNullable)); } int _remoteFieldTypeId(Object value, String identifier) { @@ -247,6 +390,23 @@ void main() { } }); + test('compatible mode round trips unsigned encoding edge cases', () { + final fory = Fory(compatible: true); + _registerUnsignedFields(fory); + + for (final value in [ + _smallUnsignedFields(), + _taggedBoundaryUnsignedFields(), + _midpointUnsignedFields(), + _maxUnsignedFields(), + _nullUnsignedFields(), + ]) { + final roundTrip = + fory.deserialize(fory.serialize(value)); + _expectUnsignedFieldsEqual(roundTrip, value); + } + }); + test('compatible metadata records unsigned wire types and nullability', () { final writer = Fory(compatible: true); final reader = Fory(compatible: true); @@ -273,6 +433,18 @@ void main() { _remoteFieldTypeId(roundTrip, 'u64_tagged'), equals(TypeIds.taggedUint64), ); + expect( + _remoteFieldTypeId(roundTrip, 'u64_var_int'), + equals(TypeIds.varUint64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'u64_fixed_int'), + equals(TypeIds.uint64), + ); + expect( + _remoteFieldTypeId(roundTrip, 'u64_tagged_int'), + equals(TypeIds.taggedUint64), + ); expect(_remoteFieldNullable(roundTrip, 'u8'), isFalse); expect(_remoteFieldNullable(roundTrip, 'u16'), isFalse); @@ -283,6 +455,122 @@ void main() { expect(_remoteFieldNullable(roundTrip, 'u64_var_nullable'), isTrue); expect(_remoteFieldNullable(roundTrip, 'u64_fixed_nullable'), isTrue); expect(_remoteFieldNullable(roundTrip, 'u64_tagged_nullable'), isTrue); + expect(_remoteFieldNullable(roundTrip, 'u64_var_int_nullable'), isTrue); + }); + + test('web rejects JS-unsafe uint64 Dart int fields', () { + final cases = <({String name, UnsignedFields value})>[ + ( + name: 'varint', + value: _smallUnsignedFields()..u64VarInt = _jsUnsafeInt, + ), + ( + name: 'fixed', + value: _smallUnsignedFields()..u64FixedInt = _jsUnsafeInt, + ), + ( + name: 'tagged', + value: _smallUnsignedFields()..u64TaggedInt = _jsUnsafeInt, + ), + ( + name: 'nullable varint', + value: _smallUnsignedFields()..u64VarIntNullable = _jsUnsafeInt, + ), + ]; + + for (final testCase in cases) { + for (final compatible in [false, true]) { + final fory = Fory(compatible: compatible); + _registerUnsignedFields(fory); + if (identical(1, 1.0)) { + expect( + () => fory.serialize(testCase.value), + throwsA(isA()), + reason: '${testCase.name}, compatible=$compatible', + ); + } else { + final roundTrip = fory.deserialize( + fory.serialize(testCase.value), + ); + _expectUnsignedFieldsEqual(roundTrip, testCase.value); + } + } + } + }); + + test('web rejects negative uint64 Dart int fields', () { + if (!identical(1, 1.0)) { + return; + } + + final cases = <({String name, UnsignedFields value})>[ + ( + name: 'varint', + value: _smallUnsignedFields()..u64VarInt = -1, + ), + ( + name: 'fixed', + value: _smallUnsignedFields()..u64FixedInt = -1, + ), + ( + name: 'tagged', + value: _smallUnsignedFields()..u64TaggedInt = -1, + ), + ( + name: 'nullable varint', + value: _smallUnsignedFields()..u64VarIntNullable = -1, + ), + ]; + + for (final testCase in cases) { + for (final compatible in [false, true]) { + final fory = Fory(compatible: compatible); + _registerUnsignedFields(fory); + expect( + () => fory.serialize(testCase.value), + throwsA(isA()), + reason: '${testCase.name}, compatible=$compatible', + ); + } + } + }); + + test( + 'schema-consistent Dart int uint64 readers reject full-range wrapper payloads', + () { + final writer = Fory(); + final reader = Fory(); + _registerUnsignedWrapperFields(writer); + _registerUnsignedWrapperAsIntFields(reader); + + for (final encoding in ['varint', 'fixed', 'tagged']) { + expect( + () => reader.deserialize( + writer.serialize(_schemaUint64ReadMismatchPayload(encoding)), + ), + throwsA(isA()), + reason: encoding, + ); + } + }); + + test( + 'compatible Dart int uint64 readers reject full-range wrapper payloads', + () { + final writer = Fory(compatible: true); + final reader = Fory(compatible: true); + _registerUnsignedFields(writer); + _registerUnsignedIntFieldsReader(reader); + + for (final encoding in ['varint', 'fixed', 'tagged']) { + expect( + () => reader.deserialize( + writer.serialize(_uint64ReadMismatchPayload(encoding)), + ), + throwsA(isA()), + reason: encoding, + ); + } }); }); } diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md index 87ea18cf45..6dd2ff6df5 100644 --- a/docs/compiler/generated-code.md +++ b/docs/compiler/generated-code.md @@ -962,10 +962,13 @@ Map byName = {}; ### Registration -Each schema includes a registration helper that handles all types in the file and transitively registers imported types: +Each generated Dart library includes a registration helper named after the input +file, such as `AddressbookFory` for `addressbook.dart`. The helper handles all +generated types in that file and transitively registers imported generated +types: ```dart -abstract final class ForyRegistration { +abstract final class AddressbookFory { static void register( Fory fory, Type type, { @@ -990,8 +993,8 @@ import 'generated/addressbook/addressbook.dart'; void main() { final fory = Fory(); - ForyRegistration.register(fory, Person, id: 100); - ForyRegistration.register(fory, Dog, id: 104); + AddressbookFory.register(fory, Person, id: 100); + AddressbookFory.register(fory, Dog, id: 104); // ... final person = Person() diff --git a/docs/guide/dart/cross-language.md b/docs/guide/dart/cross-language.md index a659c9257a..a4554b8c46 100644 --- a/docs/guide/dart/cross-language.md +++ b/docs/guide/dart/cross-language.md @@ -168,7 +168,7 @@ Fory matches fields by name or by stable field ID. For robust cross-language int Because Dart `int` is not itself a promise about the exact xlang wire width, prefer wrappers or numeric field annotations when exact cross-language interpretation matters: - `Int32` for xlang `int32` -- `UInt32` for xlang `uint32` +- `Uint32` for xlang `uint32` - `Float16`, `Bfloat16`, and `Float32` for reduced-width floating point - `Float16List` and `Bfloat16List` for 16-bit floating-point array payloads - `Timestamp`, `LocalDate`, and `Duration` for explicit temporal semantics diff --git a/docs/guide/dart/custom-serializers.md b/docs/guide/dart/custom-serializers.md index 280eb1ed8f..8c8c1ee449 100644 --- a/docs/guide/dart/custom-serializers.md +++ b/docs/guide/dart/custom-serializers.md @@ -48,13 +48,13 @@ final class PersonSerializer extends Serializer { void write(WriteContext context, Person value) { final buffer = context.buffer; buffer.writeUtf8(value.name); - buffer.writeInt64(value.age); + buffer.writeInt64FromInt(value.age); } @override Person read(ReadContext context) { final buffer = context.buffer; - return Person(buffer.readUtf8(), buffer.readInt64()); + return Person(buffer.readUtf8(), buffer.readInt64AsInt()); } } ``` diff --git a/docs/guide/dart/field-configuration.md b/docs/guide/dart/field-configuration.md index 4f16f2b7ad..5e0b439f02 100644 --- a/docs/guide/dart/field-configuration.md +++ b/docs/guide/dart/field-configuration.md @@ -110,7 +110,10 @@ class Sample { Available annotations: `@Int32Type`, `@Int64Type`, `@Uint8Type`, `@Uint16Type`, `@Uint32Type`, `@Uint64Type`. -Alternatively, use the explicit wrapper types (`Int32`, `UInt32`, etc.) described in [Supported Types](supported-types.md). +Alternatively, use explicit wrapper types such as `Int32`, `Int64`, `Uint32`, +and `Uint64` as described in [Supported Types](supported-types.md). Wrappers use +compact varint encodings by default; use annotations or generated field +metadata when a fixed-width or tagged encoding is required. ## Aligning Fields Across Languages diff --git a/docs/guide/dart/index.md b/docs/guide/dart/index.md index 0e814c4476..cab137c48c 100644 --- a/docs/guide/dart/index.md +++ b/docs/guide/dart/index.md @@ -24,6 +24,7 @@ Apache Fory™ Dart lets you serialize Dart objects to bytes and deserialize the ## Why Fory Dart? - **Cross-language**: serialize in Dart, deserialize in Java, Go, C#, and more without writing any glue code +- **Platform support**: use the same generated-serializer API on Dart VM/AOT, Flutter, and web - **Fast**: generated serializer code replaces reflection at runtime - **Schema evolution**: add or remove fields without breaking existing messages - **Circular references**: optional reference tracking handles shared or recursive object graphs @@ -114,25 +115,26 @@ dart run build_runner build --delete-conflicting-outputs - `fory.deserialize(bytes)` — returns a `T` - `@ForyStruct()` — marks a class for code generation - `@ForyField(...)` — per-field options (skip, ID, nullability, references) -- Integer wrappers: `Int8`, `Int16`, `Int32`, `Uint8`, `Uint16`, `Uint32`, `Uint64` +- Integer wrappers: `Int8`, `Int16`, `Int32`, `Int64`, `Uint8`, `Uint16`, `Uint32`, `Uint64` - Float wrappers: `Float16`, `Bfloat16`, `Float32` - 16-bit float arrays: `Float16List`, `Bfloat16List` - Time types: `LocalDate`, `Timestamp`, `Duration` ## Documentation -| Topic | Description | -| --------------------------------------------- | --------------------------------------------------------------- | -| [Configuration](configuration.md) | Runtime options, compatible mode, and safety limits | -| [Basic Serialization](basic-serialization.md) | `serialize`, `deserialize`, generated registration, root graphs | -| [Code Generation](code-generation.md) | `@ForyStruct`, build runner, and generated namespaces | -| [Type Registration](type-registration.md) | ID-based vs name-based registration and registration rules | -| [Custom Serializers](custom-serializers.md) | Manual `Serializer` implementations and unions | -| [Field Configuration](field-configuration.md) | `@ForyField`, field IDs, nullability, references, polymorphism | -| [Supported Types](supported-types.md) | Built-in xlang values, wrappers, collections, and structs | -| [Schema Evolution](schema-evolution.md) | Compatible structs and evolving schemas | -| [Cross-Language](cross-language.md) | Interoperability rules and field alignment | -| [Troubleshooting](troubleshooting.md) | Common errors, diagnostics, and validation steps | +| Topic | Description | +| ----------------------------------------------- | --------------------------------------------------------------- | +| [Configuration](configuration.md) | Runtime options, compatible mode, and safety limits | +| [Basic Serialization](basic-serialization.md) | `serialize`, `deserialize`, generated registration, root graphs | +| [Code Generation](code-generation.md) | `@ForyStruct`, build runner, and generated namespaces | +| [Type Registration](type-registration.md) | ID-based vs name-based registration and registration rules | +| [Custom Serializers](custom-serializers.md) | Manual `Serializer` implementations and unions | +| [Field Configuration](field-configuration.md) | `@ForyField`, field IDs, nullability, references, polymorphism | +| [Supported Types](supported-types.md) | Built-in xlang values, wrappers, collections, and structs | +| [Schema Evolution](schema-evolution.md) | Compatible structs and evolving schemas | +| [Cross-Language](cross-language.md) | Interoperability rules and field alignment | +| [Web Platform Support](web-platform-support.md) | Dart VM/AOT, Flutter, and web support, limits, and validation | +| [Troubleshooting](troubleshooting.md) | Common errors, diagnostics, and validation steps | ## Related Resources diff --git a/docs/guide/dart/supported-types.md b/docs/guide/dart/supported-types.md index 8606ac5fed..e7ee82e737 100644 --- a/docs/guide/dart/supported-types.md +++ b/docs/guide/dart/supported-types.md @@ -37,20 +37,36 @@ The following Dart types serialize directly without any special handling: ## Integer Wrappers -Dart `int` is 64-bit at runtime. If the peer language expects a 32-bit integer (Java `int`, Go `int32`, C# `int`) and you send a Dart `int`, the deserialization may fail or silently truncate. +Dart VM/native `int` can represent signed 64-bit values, while Dart web `int` +is limited to JavaScript-safe integer precision. If the peer language expects a +32-bit integer (Java `int`, Go `int32`, C# `int`) and you send a Dart `int`, +the deserialization may fail or silently truncate. For browser and Flutter web +precision rules, see [Web Platform Support](web-platform-support.md). -Use an integer wrapper class to pin the exact wire width: +Use an integer wrapper or field annotation to select the wire type explicitly: ```dart final Int8 tiny = Int8(-1); // 8-bit signed final Int16 shortValue = Int16(7); // 16-bit signed -final Int32 age = Int32(36); // 32-bit signed — matches Java int, C# int, Go int32 -final UInt8 flags = UInt8(255); // 8-bit unsigned -final UInt16 port = UInt16(65535); // 16-bit unsigned -final UInt32 count = UInt32(4000000000); // 32-bit unsigned +final Int32 age = Int32(36); // 32-bit signed, varint by default +final Int64 seq = Int64(0); // signed 64-bit, varint by default +final Uint8 flags = Uint8(255); // 8-bit unsigned +final Uint16 port = Uint16(65535); // 16-bit unsigned +final Uint32 count = Uint32(4000000000); // 32-bit unsigned, varint by default +final Uint64 offset = Uint64(0); // unsigned 64-bit, varint by default ``` -Each wrapper clamps the stored value to the target bit width. +Each wrapper clamps or normalizes the stored value to the target bit width. +Root `Int32`, `Int64`, `Uint32`, and `Uint64` values use compact varint wire +types by default. Use `@Int64Type`, `@Uint32Type`, `@Uint64Type`, or generated +field metadata when a fixed-width or tagged encoding is required. + +On Dart VM, `Int64` and `Uint64` are extension types over `int`. Once a value is +passed through an `Object`-typed dynamic/root boundary, the VM cannot recover +whether it was originally a plain `int`, `Int64`, or `Uint64`. Use generated +field metadata or explicit `Buffer` APIs when native VM payloads must preserve +unsigned 64-bit identity across dynamic boundaries. Dart web uses wrapper +classes, so web root `Uint64` values keep `varuint64` metadata. ## Floating-Point Wrappers diff --git a/docs/guide/dart/web-platform-support.md b/docs/guide/dart/web-platform-support.md index 39a14d7b10..3ca6154269 100644 --- a/docs/guide/dart/web-platform-support.md +++ b/docs/guide/dart/web-platform-support.md @@ -19,18 +19,23 @@ license: | limitations under the License. --- -Fory Dart supports browser and Flutter web builds through generated serializers -and platform-specific runtime implementations. The public API is the same as VM -builds, but web builds have stricter integer precision rules because Dart `int` -is represented by JavaScript numbers. +Fory Dart supports Dart VM/AOT, Flutter, browser, and Flutter web builds +through generated serializers and platform-specific runtime implementations. +The public API and registration flow are the same across these platforms, but +web builds have stricter integer precision rules because Dart `int` is +represented by JavaScript numbers. -## Supported Web Targets +## Supported Targets The Dart runtime supports: +- Dart VM/JIT applications. +- Dart AOT/native applications. +- Flutter mobile and desktop applications. - Dart applications compiled to JavaScript for browsers. - Flutter web applications. -- Generated `@ForyStruct` serializers and manually registered serializers. +- Generated `@ForyStruct` serializers and manually registered serializers on + all supported targets. ## Code Generation Is Required @@ -73,9 +78,9 @@ cd dart/packages/fory dart run build_runner build --delete-conflicting-outputs ``` -The registration call is the same on VM and web. Manual serializers use -`registerSerializer(...)`; generated structs use the generated `register` -wrapper. +The registration call is the same on VM/AOT, Flutter, and web. Manual +serializers use `registerSerializer(...)`; generated structs use the generated +`register` wrapper. ## 64-Bit Integer Rules @@ -133,7 +138,7 @@ class StorageExtent { ## Custom Serializers Custom serializers can use the same `Buffer`, `WriteContext`, and `ReadContext` -APIs on VM and web. For 64-bit values: +APIs on VM/AOT, Flutter, and web. For 64-bit values: - Use `buffer.writeInt64(Int64(...))` and `buffer.readInt64()` for full-range signed 64-bit values. diff --git a/docs/specification/xlang_implementation_guide.md b/docs/specification/xlang_implementation_guide.md index e83384122a..7537633183 100644 --- a/docs/specification/xlang_implementation_guide.md +++ b/docs/specification/xlang_implementation_guide.md @@ -366,17 +366,19 @@ The normal Dart integration path is: 1. annotate structs with `@ForyStruct` 2. annotate field overrides with `@ForyField` 3. run `build_runner` -4. from the source library, bind the generated metadata privately and register - generated types through `Fory.register(...)` +4. call the generated per-library helper, such as + `Fory.register(...)`, to bind private generated metadata and + register generated types Generated code should emit: - private serializer classes - private metadata constants -- private generated installation helpers per annotated library -- generated binding installation that keeps serializer factories private +- a public per-library registration helper that users call from application code +- private generated installation helpers that keep serializer factories private -Generated code should not create a public global registry or a second public API +The public helper should be a thin generated wrapper around the runtime +registration API, not a public global registry or a second unrelated runtime API family. ## Directory Layout From f025ded3977bb814891d70ef84df8b0ba7f3e51f Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 18:53:56 +0800 Subject: [PATCH 13/14] update benchmark result --- docs/benchmarks/dart/README.md | 67 +++++++++++----------- docs/benchmarks/dart/mediacontent.png | Bin 52584 -> 42586 bytes docs/benchmarks/dart/mediacontentlist.png | Bin 55357 -> 53715 bytes docs/benchmarks/dart/sample.png | Bin 42105 -> 46415 bytes docs/benchmarks/dart/samplelist.png | Bin 51598 -> 45351 bytes docs/benchmarks/dart/struct.png | Bin 38645 -> 32985 bytes docs/benchmarks/dart/structlist.png | Bin 44541 -> 48417 bytes docs/benchmarks/dart/throughput.png | Bin 89092 -> 87138 bytes 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/benchmarks/dart/README.md b/docs/benchmarks/dart/README.md index 090c930bf6..d210210b58 100644 --- a/docs/benchmarks/dart/README.md +++ b/docs/benchmarks/dart/README.md @@ -4,47 +4,47 @@ This benchmark compares serialization and deserialization throughput for Apache ## Hardware and Runtime Info -| Key | Value | -| --------------------- | ---------------------------------------------------------------- | -| Timestamp | 2026-04-13T21:55:28.456625Z | -| OS | Version 26.2 (Build 25C56) | -| Host | Macbook-Air.local | -| CPU Cores (Logical) | 8 | -| Memory (GB) | 8.00 | -| Dart | 3.10.4 (stable) (Tue Dec 9 00:01:55 2025 -0800) on "macos_arm64" | -| Samples per case | 5 | -| Warmup per case (s) | 1.0 | -| Duration per case (s) | 1.5 | +| Key | Value | +| --- | --- | +| Timestamp | 2026-04-23T10:50:07.751368Z | +| OS | Version 15.7.2 (Build 24G325) | +| Host | MacBook-Pro.local | +| CPU Cores (Logical) | 12 | +| Memory (GB) | 48.00 | +| Dart | 3.10.7 (stable) (Tue Dec 23 00:01:57 2025 -0800) on "macos_arm64" | +| Samples per case | 5 | +| Warmup per case (s) | 1.0 | +| Duration per case (s) | 1.5 | ## Throughput Results ![Throughput](throughput.png) -| Datatype | Operation | Fory TPS | Protobuf TPS | Fastest | -| ---------------- | ----------- | --------: | -----------: | ------------ | -| Struct | Serialize | 3,989,432 | 1,884,653 | fory (2.12x) | -| Struct | Deserialize | 5,828,197 | 4,199,680 | fory (1.39x) | -| Sample | Serialize | 1,649,722 | 500,167 | fory (3.30x) | -| Sample | Deserialize | 2,060,113 | 785,109 | fory (2.62x) | -| MediaContent | Serialize | 800,876 | 391,235 | fory (2.05x) | -| MediaContent | Deserialize | 1,315,115 | 683,533 | fory (1.92x) | -| StructList | Serialize | 1,456,396 | 367,506 | fory (3.96x) | -| StructList | Deserialize | 1,921,006 | 645,958 | fory (2.97x) | -| SampleList | Serialize | 411,144 | 48,508 | fory (8.48x) | -| SampleList | Deserialize | 464,273 | 103,558 | fory (4.48x) | -| MediaContentList | Serialize | 186,870 | 77,029 | fory (2.43x) | -| MediaContentList | Deserialize | 330,293 | 128,215 | fory (2.58x) | +| Datatype | Operation | Fory TPS | Protobuf TPS | Fastest | +| --- | --- | ---: | ---: | --- | +| Struct | Serialize | 5,041,693 | 2,073,839 | fory (2.43x) | +| Struct | Deserialize | 6,395,290 | 4,991,881 | fory (1.28x) | +| Sample | Serialize | 1,783,688 | 552,140 | fory (3.23x) | +| Sample | Deserialize | 2,124,197 | 934,794 | fory (2.27x) | +| MediaContent | Serialize | 952,498 | 438,419 | fory (2.17x) | +| MediaContent | Deserialize | 1,649,039 | 737,340 | fory (2.24x) | +| StructList | Serialize | 1,945,119 | 399,007 | fory (4.87x) | +| StructList | Deserialize | 2,119,403 | 764,832 | fory (2.77x) | +| SampleList | Serialize | 475,413 | 52,512 | fory (9.05x) | +| SampleList | Deserialize | 508,939 | 116,236 | fory (4.38x) | +| MediaContentList | Serialize | 224,925 | 84,860 | fory (2.65x) | +| MediaContentList | Deserialize | 387,070 | 154,392 | fory (2.51x) | ## Serialized Size (bytes) -| Datatype | Fory | Protobuf | -| ---------------- | ---: | -------: | -| Struct | 58 | 61 | -| Sample | 446 | 377 | -| MediaContent | 365 | 307 | -| StructList | 184 | 315 | -| SampleList | 1980 | 1900 | -| MediaContentList | 1535 | 1550 | +| Datatype | Fory | Protobuf | +| --- | ---: | ---: | +| Struct | 58 | 61 | +| Sample | 446 | 377 | +| MediaContent | 365 | 307 | +| StructList | 184 | 315 | +| SampleList | 1980 | 1900 | +| MediaContentList | 1535 | 1550 | ## Per-workload Plots @@ -71,3 +71,4 @@ This benchmark compares serialization and deserialization throughput for Apache ### MediaContentList ![MediaContentList](mediacontentlist.png) + diff --git a/docs/benchmarks/dart/mediacontent.png b/docs/benchmarks/dart/mediacontent.png index 17bd33a9b1e33c312b022f325eca4ddecce8d364..ba6e2d3aed279fde02aa85a8f4a8a164dd43c0ce 100644 GIT binary patch literal 42586 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 IeP^!y4>3!B_W%F@ literal 52584 zcmeEu2T+vTmuH(mMNm;ml2kyEB%nx+3K9ea$w@$R&anX%K?MQHNN7;J?EFseXgu1LwcO%I0}U#mA!LI z1%)C$ib5TnKS~6D^PyC-2L2FqzOCu3YG>-~X6R^wx@+icZ*AvnZDGXdYU1c*VQ0(B z$$gEJpPkX%+1cJnkc-RaUq8WV=V-?DQOBhPUgenm9W5smijo}p?|>Pm9p3AEr|d0> z`|b&IL&VN!C-Hkr=RK$VFIHbtNpZXV`px5`2j5=tIU;deIm_tK`QFqQM#SeNQf1FX za>|`aIeN9dlIUn++lm-|8MA{ky?Mbu(geFBc-btbFRm|ch-ogORTv-e^~;wp@P+<< zXxxeDC;s~h3dJ~i>eRu1J>`@X|F<74KYHN8zy4+P;$i=PJ@E_r&;6yQ6xDP^ataF5 zvVt4dy>0Yfo0BmfI|q=@$NIwW6#P+lL*mJG($0iDyPD14g#*RrKR=np(+Rmg8~C0i zqC<~fy_cz+{P@5;?z6YT&z5GU(iKH=IL< zk8MP&($!{jV?30Zg@t7pN5f}ro}92=5QH1?-HXBxdnsv`STaw3iz?a)2n>{;Zck0M z>d7s17_aBY6V?ap2CT(z7=AiDD?)|&9>?1(!+?HRL}J{bl9E@mm32Q`{cfRIt8IgF zngX@Q_EH0fW^ho)io?&hw;i`P9H_57@Ef%MOj=sKE>^YDB6Mh7w6iDW)^qYC-&ynm zefXBU_13QU;zwe27Bw?*)5b7~Wo)ZmUgEmJiGV+A=7H4n{Uy^wOS&Kzb(LAC%r*++ zyW7Wgcr{6cYUDdVhuT){-i{K>-PaRZX%ewS#`4kZ1za3QwmzQ}(=?yDd#H|6?}K}i zJVwUrr$uXR6q|;2pCak`Gvj?`m?*BAjZgFG@-ZzT)18^Bt~FaXYzO&CSZ;K#y6$d! zkPz-iu-qj-8zSheo_v$^f?e&7t5R<)t162uMzq?xukdTlYSkgSCuKVXyiR$>?Ckm$ z?Y(>VC>mQ5g)~gR#~P+8)15wjnqrww*u8Bx2VY|M7C%1hIzPzDpDF|UzP1D@F6*%0}7ugacI^VWq;g>n~rlr3l&TmWG!a{9_M$kFq z;g8p9qT7p7)Z9k5vi@w|!)ym7k?qjKMA~@$aHpS=6kTAzlP$YM35}FHulUP)`W^|M z;WDW4rr;d%-P_?KC`>C9R?dHrr{L1J7CBkFx9dexPn)iUiPq5f_&P7PG5UmpVn%@{ zbyD2GbDG)Xcbpk}hTI7bx6MvfCE2ep&e;$pw>xz?CVPGtv{L;1zIQCcZTe@HTDF{# zMND@Sfhqq=#qlG5Tp3P0PTk(em)6EZ#XO^}7*lzV zbOmwMP?^OklhQ;yA@WzuyKN`fag9|@Z)Q%WD_2j5gw?piCkU2l8KEEB8dpLU-%`_o z;^V`)O|35J)cT;`hWzx{+git2NtiO`=#-VVX*55|)vp%(8hRSJ1CP(`w=q8}b!;B5yb1)hR1ysEC`w6? z(k7C>?zd73)iP7^Y)%v^!}=IWaSY9WdP))_BDUb0(a}FBr)EwODyg|J*oJiw9|jZ?;xDlte_Z32{%C93=C9zLSwxQ21_Ao3O}Tv9s1= z&|c|gM?$<%fu+}~a7x`W??KLBI&^>6=2p94>pg~#{v=1IS#i5XIqSFYFaSu& zu%lq9CU~#>xTykbvMKDslU6|*a-z4`9A45cD0|G@#cqbsV7YWYbrK51MYh8uR)(3^ zcMc&}wd@ij(!1^Nb|}x)pSLR>_Lvz9WKe8#y*cC@G1QVEzY_4FNp_CjHWG69<)4VL9ImRs#zsi4&-IDmLum!b zv7VvA%g=^<*89w2IMQGD`UGou1tVAL)cd1HksYkNGG9JU=7Aq_!?D>7R~_q5lXyau zw45U1xf&*l-fAqh84wP|K-G=rGO&H5Q(*L!iHE0sMbq8A`~)q3+r016SITbQ#kORr zq_`(EG&Q?hv*EmEEo`DOP{4<5Z}fJIJAUFIWfj|+x@6Iln>Yd8rUkb$(R2y=L+L3i zDBmj3@I7`nu^dxQ2W6^vU|n%cu&HH5rk{K_zSO`FL(VMMSmA`7E9p{hk2B1W_+?Wz z#4=E3S4JQ9g!p81N=jZFuaN!sSZ>GN?M=T~3##|LG9_Zh@1$Qo?98~Ags(d`zgoVB zuicZ~*;-Sb{C9=RKIf#^w(T`nAq{cAZk|?m#Z5-``cBQ+Pn@VRt`HLbB3V zs*$I^Jo@Bh|IX=4(wYpSLyEQf-b=SD0Fk7sWvgp=E8Km3ed+sX$Ef0G@x+{rqSX&B zv!>O@018dIy>67vLmqjWWvUgimtKmJvZ&<0bY8PijopaHr zOl`(~k2A)40^IaiZo14t>#EOI-8H^Dlbz>n>YFz4r%C5u+}|6c+BA`9_$dl^Xo*b+ zKao&!Y96~wx{UxW!x31lBE0oggf=t)?Dfh!Yx5{I zz=7t=G`B@vy2JUs>v%1@VcEx}H(tlrwKWpH>SHz#|fcj6p5an{3 znz3l$Ay;Ja1Nnwh0}IVuwcfWw^|DuWR})qTPibDMS%42JE@ylq<~r49J$hq)lx+lO z$w}yRf<{J058G2@d3?6rHv!Z#^YEy&r`=`i(Q`{Ydqr6sx;gXOY)=f3vOCZ?RSEW| z>q6*+9?ta^xMChOSBKCFTttuyq1p={ogm(DmGtW80MZC<@E@5#p^PIfT=PRjt02lW}$~o*O7rTAv?cgW4EzQglPy5!=Fnv`E{b4;Aw{4^B{u z)!QovkD-`|03b8UZZG)m71=ABotO!J*Ff*3fG}2z_LNsE*w$uXt*p=m3;LdBPA}B% zDK25Rmz%j%w*~e=gAkAd1B|ahx&6m_WhZP))V+7oP3yRUNu*yh%SShsJB(YWCF@)Z zID}&KXnS%BW#7eTH_Xm!`aPfEsWw}=P{U^OEwaGAElyT5Q-yhYq_+0GUZwhcHC|ue z1zB;=p6I)KjK91o2LQnYu8QLb8sFzwJ-Ynk=E#H#fIW?uP{i%KuCF>3TXxM9&ISes zHf+X{`7nUeLqb(@B4d4|&3@@Ar9J`@STg#yCYmBr;ZB8f8HkdwR?C~z_l)Xebb4$C z%L{(^bdGlpGxXz)>--Kli253xPqdnr#=3Lj&ow?eL_Al%y5Ts{cp;9*Bs`p1UJiQP zLukhu-U$Hy%63^odK*p|X!QbKlnSkHkM}V$*N{A(aq7n&<7XX%tHATw?N+4rSXQz3T#r* zvfLIr6Pw43)_AXZ+AQt@7TrNO=0O&iB>$Y8S$MCTFg!vWBcuVBn~8&t5)Dvpy~4mk zoa)@UbM`|;X06(?Dm%J)80r!`Lx5H`34C7bbNm>o=GQY%leVaDXyodKL&GLm1F7}! z05WbJe2%i-K6nf^;3q1wx{?6$cPKxm60=r%l0b4PDN0faPW`GW>Z4XiR~i_6dt(A5 zicEhjp9nrQ-IIqoK_}QrbcJS$zliG0uNI+&NKljd(WDitnLuV?wGs$y?m73?_GwjHgNNQ@*s`r}LJ*gkfKT#_H#Y^A( zoNmrsz%5TU+G^t;S~jihYwRkTtQHc1%l(=-PN#W1zMv|!y<@WT&7S! zxrN;3SJ{P}ek**h@>nr~`e-r$aG7It>85#G9es~+MR>!nw+&%pzCHuFa3e)Sa|~)l zS%ifL>Afai$r$Z!uPAvXCg47}E!c*p%Exg2E*tR)svzI`z@K-|JE2F|tR;aPYSwd) z`3kHN87=>VP&yIM>%YHJ8m!J?i&{lj26mGJ>6fd_JQEgb2)Q=tt2;wwc9hz`{5!UiCjbR8f>dFdL|f;ik@=Ow~| z)-CHodjr+thD}`5uqxuVo|pF#6o(xjmPF$of`dbnltJJZYiL@2Uz|i$FR!*bZ3zh2 z^cR=bgy>aGW5>_imVczT>diO&4)U`#dkVBBa$e^tU^j2K0cLT=o{m&HOC!&A*8JMpp1KcqBQ;C(X5I(JCW*$89%@th zuqpf@>CqckvNa3dUbgS*X^1})ZFz+T%2{x#AuiT5x(ELtYLueTB%qC4gEk_ zB9F<79N?VvtxwHnLWJh?C!ybZJ`R1toxoLZA9HR0s`N{qM7ngFDCHqyvPgEVLe0L$ z8&=)DJh(tNXhzy2BpkeNCvA*6mczAcq~!yLT?cFq;PXe1ozB687N0N#oiSA=lzyGcZ(=giD|G0| zB5bN69Xclq!XxqGz>;X%uEOLkug{giHT|`|zR2|)+%r6Z_x-yEJf`s@>5W{hiunE+28_^l!xq&sYw~{_&irav_5b43wf`SMkeVy7KlZD zi%T$}7+2w5H+puk+{8J(r_7nMX$ohaghdtxQivu-xB8lbmu$mY1xkFmuhK2gEI5VI zyuNEsM?j(`cfnh+OMmVRi$WNJp#W`640|n<=nwP<333LH?VdC)-}}5@!cn>B4#TBn zO4WGfHrM;JrgOR@-KKsBwh;$^XSP~N#m?^^@yt+?N?U5s1mW44p2@?h+Dq1pqd&g@)g9=>*+6D zp|jJQrW)HNEX080@eNAwW(_i!C}wY9KQ34@;rjPi^WVvbA7g+sWW>Eq30K3qWexT0oLS_kzY3F&mg!1 z#^I>z6!V`(ZNnWd$#cc^_kD=bre@)&o*7}K%q&7dBT4d`708Qa-IWwDe4R;wOq*u; z5ixzf)+2!ZZ>knQkvQbeR;hcM*gO7sc!Hv3?o)G=vkQHw$oZdfiIgMnJIlA$Cx68E ztO!~?AKdsRz+ASG^qIiDu;SJtQh+@PmY$&3)XSU0`Sfkt6!&XqIx~&cP8{5vh)@zv zT}7pkRX4UD`wfcGl-4}ju&ygO)A>Y7>SN!HF=8v#eCs}*S)D0erZ<%6dLKmudsJ}; zRQhw%`dJ-8c*j?Fd`eR+_dBISb|s!`mVHn*vZ^jsx-FFI>|D~))tJrIEV$T&(fG_! z-EZ(&6t}Vjb*sLzW<`6NfylHqDRH7(pJ>BH`t~QBMZP-tB4JC6}0QM29 z0ymrycQVIU2C9q++?%EKN>ylqOS&BIc}cl$9{N6gFI>5=$P7QI6wAYbOPH?Hj``xbBq>*cM?f$bi8sY?98RltgP^3$U^O#u6F;tS1zMXh5X$ zdHU7woe3ZTTGxlEySItWhtS;kmQK43cfq0z+~r7m5zpS+cM;)xM{FDHB00cP!h4qw z-0}SR^C!h{o>o!)Zcfp5l^7vYhTh{#hu}vou4H5X4bNyxd6Zf3FZ5|@WBz*kN7w$b zx<<2r(EZ%K*7^1}$tEyXwC7>Vsq{nlccdcu>@}dl6pFD6INN87a3!c;*a zMz+uPlFTe-`tYkQ?O6u>;=ORZLiNPkF_(c-8{7;I0LB1M;#jiSWzTHi;-59D1p<$z zu^3<5YU9CZ*O5!2d?iG?fSlrVsIu(_MFK~pIbTMmy&G!hV(pNZ);}Aqt%uC z_UOal*Wq<)4QhQH0V(yHb%DpD)s|->S~=lYxlWvWLR zcUx<%;Je;9=D{itr&iwXxnB+=``_mt=5CVne_^z+1g4gMuAc zNZde&@N<^{jIV1y4GbhysY7<+WrYf;^GnuXko=AX>{Wl3O`Saru7Sqt>shP0xyz*+ z`pvz-kH-5#YZzd8zmSdLOf=I1Z^8(eDTV!jlGsis*6SMB4oM3I&>p@7!zP)Z-8_PL zZnap~8iw;XZE05qom%^L25c*FFKRB6FAL6aPa8w~e#r^~5!UP?6fn_-RnT6D2k}rM zjK9TjE#O6R@N2Xz3bCUeA)qL2*oc|8F=8-Z0q^u~!K$@Oz#c*!CEM~6${pL)v8y%- zg&l0=V2c4~fsin^fxw`>%fi42d}cwq=vdbTt1Nu#1j@}`lw>9bZ|1x6Jq3x1md&M9+BIMLIE6sMjb8#Ys8cBX5&>2}_7SDeEV`fHkq zm^0BB=uTE5PgXb9O4`S}duojvLY4vhd6}B(KL;y!#FS|2T%%=AuAaKw%IL=@;WVpR z(@>~-*##5(-hg;y(rl_BJv+z0lho!*+@2~=8ULdVAR}9RChwSYbV@X-UX{B;0`E~e z5Uh%LQnt`&}j7n^p+~E@D)t zCFG-KgEz$uwtRE&dxT+hO3Bq*)-60as4=}Rl=vqB4536O!^iXoMyyti1PQ(5g!`)> z`+1yV>8C@q&u+(3^Iz6V{IxJ5El^4XZi1Do0%UW-xpH(O4g0wE> zlLM%*eh^RV_GGax44{r2OW{3rgLZY^@EA%wpG~(sf2h=UsNXZ1`3vdI!3W9jTF5$m z{0k0!%E-v@D#2RXa4`tb=Nm1gKh9`(o$7B%E^%OB~DIFbE z>?;pe=-O@v7e}ir7;s2J${W11Dj&Ez(okDDm^$~yS3@%zLg-d>WaMA8#PK$mY3=Rq z;9T3@NOTkT6`%>ON^v$jd#6!;onNTPP~;(qJ;g}YucLfOlJsE7!+zM)%pkU@K=c8T zDYhZ!q>`b0L0=3dp7r{MRW#V*4J$6|b6Ps{)e1ch!RK!(W~t{~W$<2n1O-Wf=7uE` z5||kTKT`!9<=LB5LX7V<@{+1Heg#1uihiIJgi*%^I=4F@Tbe*aajgZC&+0ReLb-!j zfI3D=aVEn`0B!&%NG#?8*41yAMUhY(2yh;|IXj2NHYd)2v}uRPqU3aR9EdCsb*$^n zVhv$_WtDA}&*a-#YEkcUMB{D2h;f-TM@K=)y_4kr>ljo-y}|_K?)s(dU)lY)fj2t3 zf7||g_J3^PwWqEh810V%K}i7k-&qdr>m?_VxSKdnUx`%=C>6I6?OK1Fv5x-){Pj_E zWw--v{Rv6k8>Gaoi9!u*$KY@MukC!#!|a@EG{ zdZHWZr97+*W?I>aZIc8bXHU z9I_qH(ec8EMCrbSq~o_ICq=K^9JGG|;IzJ;1yt&qw`c%r()6BF7YK(mp*&f8-_O+* zKvIQsLzOp2HM86nhWUuvmy&Z0N-Wht0=-kWWC&XYv~k!zS_eWeT)isCWBT2Ec4~tJ zR&OT+gN}M~b8|aF?dwM=FNkd~ex|6R6ZO^vEsqmKFih(fgU?2Cu~knCQ8=jA5#2dD z&5+sSxHedWt{mCLpx3o@l%yI#=2oG(Y0D19Ix61wLsQbF3l!{{j5jPh^Rbx*wKYg8 zhA0E!iyQ`!PHEjTK(9p&IyQ3@I!~)q?5q#qJZ>m=8uy%XhAnu^bNIj^qQ<&Mhd9{v z%I%XUe#GCD7(ni^xJp|=E8t!Bv)|zgyoR<*6OQdsX+a8s`Gr- zS3t%qWmNtw*Z_*D)q&rT8n8N~7^wfsb8%F{%V!~5b;NU)gM=O9@tfKM(l+Sso&#lW zy#0XD`U-|1f8%=28r-nFUuqEJdB%VtJRw;ie*6`*G@BEVh>TW#MNm+i2YAOQda75^ zY~kxVh_1bJVx%ZB!W?hwvfHpnL%mgtyVA7NO!>Xf^u~ z(B5(wJ813{=JlL;vB(u5a@{UBXl?<>APoAI(iNiTSOcQ+Tw=-paU7{qn`a~bd;26oFqrSUKu&d2hIh3<-_w#LBXvU`M4!S_GX_i9`=r94aI!(85wEjXHV6cHwLeL!`8WTe2k?ne<=xX? z6OaVrkL3hBUsc8sv3_?_+ z?uH>a2}t82`30rMxEah_E9Qrmof%_V3s4JP&u1`Tisrx!5uI(|ynAM3%i-s!N^`II zF=#3?oi2)BPNG$N2OY)et2>6tHkj z6etKVP^S=X{93fO@0umQ_WX*y;VKK3S-YQd!JceY7ilBO?voSX<&=hBN91NU=B$qfpz)eOYLnuGpo!C}fJ?pM z9GlMC3k|d=*4rrP*|U5JyUpdv$war|SI|7Zc!3HCSU0DgM9_t33<7zRi({XCLahkT zfuQ}6yj2}rs9WK9tlw_7JI7{(M%eNOCFu!RRf7S3NKyM1D0m?LrkPg23FR%M8Cl_2ESQnU>%cNqXB;X6}j`!zu;CBivrX=%@HpfUwV{N09JVxlg)m^Zaw zw`kYgY=`ufN3>N_J7Q_>lYU>?XuTZR+2+m8YjlmL$AfuhuL!e>E~E!{MO{&_iic zHPIrYRluU1X&|NyTr*8jd29WOn2qs2G$DW;c~wnLAL7kkHpus3jGcCG$*d#yVtK#_ z{k~-8YJbQ_Y9&@2kkFj6ek#T0uoxe>H~~-To#7) zMEACxDzD!E%+GPbY3bK(LRs{Iu1Coo%KBzK7Snf!(qMjog36Lh0%w>}bRh5q^{*nk zp%GOj5fcex&=rs{pE}tpROAviSznkBn*iIh|a}3zA)rAvZkAoY0SZKD2wuzr-5#!F+0av z=HxAxD(B@1S!But?y?FX0A{dq);$#I4k4EnLM~^rbIB_&2J64c95383&->)R$_6d< zsM=wF)Z-+wqYwlzyf6s2d=ko0;V@e9`CZZtXXzv)Xj%3~S*jMPPzS3hIJA4~N?`KA zhtM-gGxZWQ2~+ytjjukNQq(NfpI;w5_Or-LiBR- z+qy+VHS9kAav79c?PpZL2w($6fa0NjB@-KcO3=MKOqsYB!7D&=)GU zZLi8!>JFMm0=7n9b6W2!a~QZ8+E35ndXPZ+fV)zIt*7B+hmx2LrHrSODI?Kw;;0ZT zCnaM3mFsCLz{8T^aIOugd5fKU$lTMZ*BgTjy6$4|zxx<{h0=3B`;5FH3dQ*+pZw=Z z`JF~$f5e(9wj0q$BDd8Qj=#UNBK|s(%6T)BMaiif26tJ@E+53aS>v>UJrEFr!$0#t`EoD&$(oEGbP5#-6pazZLXDXG^)<-B)@ftWF#BU(bqj))%aBab zC7F;f6?ctV5$5sdk$gGD>y%=Wuoe=CA!$Tt1ZddNU`jRu?4Q`)TycaPKr^63oR-0p z3NSI6fCF;eu?AI(R@hzHXLr+#q#N|`D@cv=q9OZ$oh6&=!VR|u)%pC6p4fsk%q0l;qHc}LVyTRC@ z4F={#L%y!wNy(xn&CJVel zg!v8~O?M_hX`S54dBN6Y*yP_pH!&F-EVk(IUcUi05Xl9w1Wd=0q|8>U!GWQunHC(r zo<9I&2B=iu!CNu_UQFQfx%z^LE;=wbQfmP693yb4FmP?^OxXm7J+O3a=LKP^N65_X zxxnSWKNreBH;nYCi?o5{6uVgJqr~-zj>33kDo01D*;%lfs9FF`l-Q8xdYRI<$*}i6`-m11R zPb8IqGv9D>Gv4}?{QYvroiNu3T8ji(VRJ(QBof$8~JYspaYGA@Ow!$X~&LnJf&sv4e~Jd zKuo)+4_MhHS^**8V)xqo{vLN|zWEpmyz-<`8}dp$OWQM;=95 z-kZyPb6+9Hwz($beD>=7M%#Dq-Z5Xj`fb5sNFV>snZ@+Yco$EWVsAFEm#=`v_?nVL zDu;TEe;U8Ii%%w~JZcGa`i1`G6^gm+BePhUu!q?Yi)lUW&F464rfFmGhSF$%jpd>* z(4A6@y(S^(HqVYsBoS!r0lX*;qw#u3st81C&Ql1K z5ZZU1UA<^==x}-i-}3|k=V_l|&qsKM66p!a;yeBJua54u7yhnmmoM|A?Q7l=^BIf0 z)7dYN=cPQ|r1Hg=PJ~Q^K9w+<;EvfCcEYxHWXl``!wdTDDp}vb+o(bFjjx@e@EODR zD`>F)8{ow{j2Rl=kfMu(%`pwo$XLP7j)b);jz|sE0*)yN_clGR7{~L~&KFVwC61|{ zF7rS^g{ujOxx_4eNY8L~l|>geYmOIU3-AmzfCir<^Cnv8WsVX?l*fC+UMLMHetUF@ zMTc~nV&c_H(!pU69NDtrwducZ0cpvcn;y##b)sXPLmRs*PrTZdXxIsoOR9|y00aK2S5 zH}yIlEV1=OSit|AB?c>2a7+U-HU~xL*?a6Evmssr#oZ4rlCb-9n0MECJ@L$^9B`@Z z;)g2Tnjwb$*mj1^{HZpp|rg9x+Tjk#_So_;;E47%`!uusW=d0RQmTV_JI2HP9i;;#_F@)Dvc zw=Q0~gf0bjDHxJUQb?c`?irq4X>LEFx&Nk&&QGnyd$ZKol#cxbltvIP1+@TC5Ql&V zCz^Rcwh~wBySFQPAz^(XUKITyl2fn0$RKciqvOFmDEh@ao_KL@TuBcTxE!S+H*v=@ z#uC>VokOH7;@T1SZU@wYO>5OR;s+qX^CUsUOC1q+u&o%pa9V@cXx#zOs@|>XuENdx zXORth{`~o?Q)@7E)C`LJti^;T1PJ>emIdP%3J?*z5-Pft;SFL~(wa*gQ2>C>D8RJP z9PYlqH3dx+NjVhSk4a|gRhEvdm^Y?NBm_KyjURP_nx_%S`a@p4bO=2{pJ+>Jl|;I7-#s6p0FcxbKYjY-UHa1)pk#>k&<8an{)(td^^w|=#UNHwC`;eE zwP796>vTMtCGL5<&_gFXlwL-VvM{-b_i8I>g0RpGAj}? z{1FNLKMt9Yq6pssHyIhDqZae2QsU1j_Q!sCe#Qhk0EAH_;vfnHiaY~Cui4aSG{U-( zA-k6v;P;Yj{%*zCjU`6!oH$FC<+(Pa6{k9chuUN9MT!p~qq{odL&!1U&sc_08km?W zM$afnieLZ@%C#nm*m%s@FIu+z15mK0n4P+4Vn2Bm#dIlG_Xz6n^EZUS1xE?I&oz)* zt{Bo1p^lP+cKP>mMTId*e)w|bUr$j6fFS>V{AW*t?|+W}y2I-K_aCpWOFd7Y118zi z09{BUtr3*mb(d5miay>D8UqT?9fyN5$iyNFcxkuxo6_4npU*@C#f*u5)3KrqP4R`}1*PzBR~_M)Vp` z4h$YHj((K&*+yQ@J~dVXoD=w;5}12pkzGe@%_F+{FpSM+=!pPY|6^w+08cB0gEt9q z^2?vOVic+Vb24hHw`*zWPLAax1^HBG(O9E z)H)5V8ym@8Mi)9FpvTX=`LM4bdjj)2D2e=u3U7n(aViAgU9%@|BZJTS2FPDaJVi_~xWi0ud5P>Q zaeRFrT(X_#2em*6ueSon0mV4=*AW%=M<9B@jMy8(K)*zrl@bM+0W62XSfx~dnH?9# zd-=Kl)2B=QNJ}~@IITiJUKm#UT}Y#cgV^b@iw3>EG6(vCSs!8*Y^xHVq>qFKqjl__ z9aq)jW28?Z9T#C#FtC5vy9VO2EFE#k%ZPva>vQPh;CBnbMyQ<}Zgx)ekEB#X*dv&E z4HfhUkk#ndNkMVu%o#YI9^h+oX$o-@P$Xhv^gU%2F`{%>KG+A#zk;~>KYVy;OIZAT zvU#}LYaE^XCE?89p9&?ZBzWu|Xj?Jh^IaJ=M!q@3GWvk~%@TyWA>06?^Zx#Dgacf3 z_V@ANv}^+&-~pWo!F(gT5G!H_Z6Qy$%KaXkB0&MFV(52_*hrEuyu>opu`L_$<0&$A z%5o9LdsY3tc|(C^*HsdB7oDAN5HD-x^JIT{{$?~BGa&!y;E@y%b_$If$PhjDdxAhm zbPT&z`$7e|(?2-MT|+RexPS4$=tC*BGs0}(67(xbf?*+S(O;Yigi{C6VqxhQOnTM= z7KCDH+6N480ys@kUPi_h)_ZULX@y+BKmG))wRaiD7?o^mHcd$m!UZrN@akxA?ly!5 z%~7QaC@BU;W}1Px+pvtmR+uaL;dkKBL`jc+(Sv)RMG{FfkTwtBHUdH{z29Yl5vH?2 z4D(3kP`Sgq!%v4V2ys*RohPl0?U#XNXYcdB0^x;kOkd+fHtG(YbpIrA+1%+d|K&TX zG>tGTa2IJMV6nxu)*#t`I4^+@1J(%)E-4QC5Ns;N9yXrB*r?~~T8urY@!k}k&2XM> zZvu^-8B`k{(6vcU(3!O+A%j1CKoNXL;Fp0<%caUiJp>7%5Q2Retlam1V$ljtMkJC8 zo#@B@=?0?JJ7DBJzz>vIVapOBB^FFA+HCgY^*JmbY}b6zM}k4Em3eQ9?gDk)`S#2* zKVwoFMnTw-M73E3(F-6PQQ&UM0-QeM?EPHiA9R_s=ZHUG#DXwosZyxqUeR{_#rwqy z>V6cILV}G3Bm*l!yBmh8k6kG*1LUabD?v)U2c__^&6yr2X4CjDN&i^R8Z>3mlxs;M z86dWpls$TU9XWMnwj-UF5W*m4+gx0n(J~WcCuCN!E~qf&l-kMn=%4V5BeA>@>Y54Q zf20f}Xt~uG#t!laK#Oars3x-PFH-tk41+u#rO)kzhNL}+?PI)hBl}s}1ejB(O|5~5 zM?nIk2{zgeeTQgJzv>bMaLK8usqG1pPmg=Zy_LRwds8HLVAdx=nNK+uk$?ZAp47eP z@|?d^_ERfuA;D{q4Ci356yRU=bP!rZ?Up=*_t)o<3d5 zC6tb;ZBWIz3GDYUdNHN#kfR?)CSgb97o0^3#%Mn_i`pMD(D@QJU=1+)?rvwI!BP9` zA^w5{VGO5Z+LF8gSib^~LVLb;&j-_xri9^y`GUe>f8_X#I-C<6EK`JY{{2M1DZ318 ziu{J+&I~7$yfUB}`?Ar1FDxc>h%p=>;0PgwO&eYiZjjvVDsDgn6nUfO)!lr<&nXBs zg}I7an_?M5KuXpl5YSUx6<`kFEwcYpqbslCiB8P~7%1Q_LGR%g!&mCI!RbMtQU2uh zhH%`I_q^NPaKy*ex)KBYdB3QQgI`}V04@9Q;4X}#QKVSB`F+@e=BVHSL)`&^=90r^ zk{Gts(qqp5s7W@t$Bx!+&9GG>SEeq>8;MSU*!T^S;s@7p_NJq1a5|5f2Lw0J^!T(L z5#)3fWKazf0iqHEU)FS=_pucGlZH;d@KestfJHmJzY7oaK-wS%SoOfp^D~#XL`0zi zSGs)j7t}5`AA^kniO6pNgeJRn>`KC}t@FVMAChX{uwDVg2LV)hYxR-4H-{#)a`7&u zzhF0+fa`Efu;$-wo)|bAg>(=(O9r~!@?q0Q<`}UX?u!rlsyvF!HbGjPK-@7Hj6i1V zyi2G1OR}SAx_lnP9$sP(4GxZky)*C^*@WpKl5OWiK#=uC+n|KDS9`g|d-Q@Hjhu6X zEkHQn86{yuyw~cK;iLpze}p~MP=ta(hI~s6^Upmk=N;s%sTo&zL5}(f~)aqAz zHa$CiDFx_Yd$EP8fYhn~gfqT{${ENmhWURDjqbM?gJ

o8Sz6dg=v|WlEG=y^l@Sp+jg9AEl(!&55ny047Y~iW9X8jh8>np8d@?bL^vh4r{ z>9q~P+z6OgX#^FY5fO|5m3#}kAa&68-2qJQK&I;D+0*+jxZP;o zBOW-I14b2Ue{9LKgP{N?LP>-4AK?wD>caoYRA8AdK zNlX?w$Pk5+eE@9C5gbpXpn%fktZoFoZW7SWGBDmnPvj%rL3}{u!So0)U>`DYg=m`) zSQg2<34iT(AG*UL(3WN7j47}{m|+r==F@E592j|I<-2gLQ-B}=>vSbp_U0#le|>{( z7~3|Ti1=KPrRqrXxEbWfkG}D(H{c3GUK=TPWPla_^ z+H_?_0nk=JCPYd45$6Q6cB2yjS?F4DIpl&k$_en9Cy+s6pb<()(y$h*S!xY3NBb@C zzEWZ4Q3c|}NCV8vDhF1hF_mHgtWI8Lm9Nw?r4!&FFw#76PVHleG%*TeFwm*tq4Q{()?4;`-2rQbo?m!`RHHCGgmkyGEh zu_!>N={cRi^Sg9>NhqBpoyh<^0c>Q&Gul>_FMA0sw1wkie=LE`R#i!#h1<9L7L;q9 zzD57wY$~C;>G-0TaFCqDn&4Y|Hb|jw9rZ6sm=8Z*f+JU4kP~cObzfu%V??R`?^+}KP z64SfaB^gh(C6|WqUb6<-R3U-?9zskZII`@)iEOn8@FMKuor{WVbA26>l|VDTfzyO8 z-Gnp@2Q9m5S}`C&q2iLz63Z2u1RoI{BAr3+bJ_<0_fZtwuL3Fjl( z(^H5MoNbA3;p=QuG#jw+!)^exL?-Z}ka_$5l9*f`$8E6IRjNH*5CI-hwvr|Z_=U1| z9-0Y@V>AoD*I)!0w&)8@l`7DuTGd3ez)qq-S>DOuC99tP-U#4n!1@#DRQg>4daT$N z>WK%a&w5tAb>u{qQPI0)TB1-YD&S%$Nds&vEa;0Oi&;CUOqXY2LRG!N%U5l$*5LRd zNq>r3Wq}ylYO=or6^ii=6BfH0c2~t&dIU0y@BQUr!01fz%Z>;ov+=LL**o-ojtxl@ zXe~F}U`V6!A9f&w5*E&oT}_-uPRWbsL(Uy^kA|=>ShR_SS*f=per^2fM-J{n*-yTp z;IJ%%k^f^Gp=TVKZkZ?TrSVEW>C;`t=Q>^i*ACJVjy+h)5gth3*^#9_>#pU*a6oc0gXoT5`VN1v7SA~Ef5lX1U#hWJUX zz%k-(1=7Qe(JAY^KaUL25)h5`tHDAh5}US{&zxK&;YTigC&C%n{0DsiSd3U9(6!mT z$GwITiCNEH6d6S5k zK|TtV$p$RVok_N2kwl;KlTG>5HNWxyM79t~roui*x7sRsZ83Eo24;JO(*kDZ8$LBkJem$kwig)HqGr{wqdMXOeD^2pWlnMAP?gxTmb#Bk`B3uGa4d zK$;zI> ziHkYi^}yGX)N2CAFS-Jq|R9I0CHMZQ-FdL><5bu?bhKYR#9zw3}%b8BS z4`4J4&9+N6Lk*ePf5UM zy8Gyl(?|mjd|79KZZg@$0O;11M(w)NwA&nO=Z};Bqe4jRw4P|74E0M-)dSb zC$`xO+bX56Tu|_UNp|6a6T3V4=Mfn28$U^hCBFy2?R9WrgIoO9pRaR2L3+wx{9Oa~ ze@4^!tMq?{;GcKP|Fa7rEWFGf&6j(8d^~p!Zsv`KnSf(N09WosiQVwWAG=WGynxcY zpHd>Bv@u~lJ?0rRUMjZR`bp5z6R^32lp|sXW2CSaiLzzwg;mGN#24A%HxXo@ul(gq zstx?PM7%t}FMI8G#CgAenjkRdFgR7^N8E zkgu&Dr2=|oxbBQ|2w=4Y9N>%>p4~!?6fy6u-9-#z=%VTe0UGWr_zmUaNZ-A)>Q*cc zIWJ{|XWiWLqU~=)i;Cu+Zasl4xGKUleZjH#zPa%&cj>B?uwJMZ%}wL~HEz#LXgOt+Df2ma- z9L4_|ixBI=%5E9Lp(VALYW0sL+9He7@wrJ-d@BhSs%wV`)dMQ!Iz9gVG}2^RCnJ!# z%BhnEM3CY70@M0X^mF0^f-J)mN>>ub&T`0<=wv`qs)II}vkBbx1n zp1sD?CE&z3B49HeGzw}(gb9@L&ztk<8OI8=z z`+!VdP0z1H*h^JbS~h5Dl!;<-$Nqbtr`~X{{v~%RfP^dgoCwD5UUQgF%oQP7$@~H$ z;q?eQ6zw}aSSPVlY?+0);MN-R5@VJ(U1^_xWA;9G;*qX^|9jQqWaT@T_2gd&|3TPg zWz%%TIWFlJz4Hc_u>f>fjH?(*aT{`RU5M}mUs5ZA35Bao$TxMX$Vgw%0?LWNMw|a4 zxs!0>>d6#`p7afgzf|L&&scpzw!9L6D<(RQ623 zT1D7?-zVXzus0{L`;~Cgw2y8$m>fJ-^~dtgo)V(%=#q;D(#Y@ph3HBe^iv(oC(Ask$<6AbaFT1M z5<-zjW-|@H-7XzO_MBYKGM2cAnK?+MdXM}tiKY4SA2Lg$HOacNOq`K3ONfn9o^IPV zMoP^5eDjZEY|D5T4}$F;X_EB25Yz^)p?Gws=I&08$FUrJ zs?}SsOnv$xWp$uBPNk{jT-*h;A#t>4^?vHj4c$nMmwxI{EFVj)aoOqe`bbq~d>zrJ z`sMABw3A3QVgUm*YX~?`_;YQ z7^~35=@`A1S+nYM$2sdpJpCuOs@`O2@ZyuoseocG4Vn^^6(G(wZo_Ageh7S(2~!A_ zwvy}QK^u{>&2{_wZvLdeDRT^$Lu2JvmH%Pb_x?E z1_@K>y~IWb-EQbzdj$px)+H&5p>5CN*xM!VrQtnU?Nsyf#)tOCzjV>JhfM4?SVEB+ zllyTM+p8;m+paYrcul!ALPa5F->%_i@fZ#hbVQ|x^(6*?oy$i#*i+e;^w9Z^hOt3s zUM&4cjP2eh&&~DOClwW!5chuOV<#t1bTc{mW>y`+^=KptfDzABmuHVD$eb0*k`oU; zGU+8&Y;K&YCXn3l{p=a(CnnlQAU8A8bF&XTrZzU#9z*@ZVn`_F$uMaVJ z@H3YqBtwc|AlPUET=Rz!B|m1P%`5WahRHvZ!eh-V#g57wqBFY0G`Fn*O-XMG%DC4T z*}t^jH3`on(cQ}4tRg?^V)2uNkuAb@1KxqWjpjMfvKYhIRjT?owmL&|g5Z(l+(1-p ziKYr_X-Wf;KWLQrbq&?xWL1N=^OB{ssLMP$d(KOzl*Ae=5OmoJk{P751PlcSAy|5X zYE^`hf*536j~ZMi;JHop@1Ps?`bLvdzQZW@zD9BY+=!G(56JM5s2>D90X-JlIYBwG zj{cH`0*#{xvB?vvR^lg5oH?tOA4mVqx13PO&uR`ZGoR0fk7xqWZiR?jh%vcyO zf18Kw;)EFVUScHQmK7xMa2;+yB8pYd5^6v}u)UHgjtOb6@5(#8y{3ncU z`^IZ8BLD$uv#Z8KHo9ITO2H5`Ktvi$L?=Y=U`aG+{+fC?`n_-*F0=j)mbPIChIApM zxw?E+rBdR%bD_xb5+)GMIy78;_qE}7kVLOV6nRijokb*^J_&olpYLzSiJy5yD}4;+ zi4P=R0S<-g?Y+=San)%8xEmy?P$XZBs4?oGTaCGaYpv+`k{C)CujAbbp^qob+!mvm zXrH{%NmfD2k#LJlZ%7H)g$M{&@&IBT1}jm({xH(14Zj+YNW*=uE)rn( z7z&`6TsO2V>YH$ai)&L!-95TqV{EFfK2_`{# z?#^(f^n@8h%7^;wF#i>iws{&)EkV&laP-^I4Fwp5IqPzVFKkA^>OWE>uDL0j_%Rz0 z9Ilu03iC__(9oQYDL|2Af!ZJ-?JM+=-DgoZ0*3(`y5B*0bIcCueJKshJCzdD z9G3bG{0d~&I9uYwDT<7kfUhk}^7PDL3U&bpB%~so$2EwH+|M)8S^gH})SuyQzRQ;x#fT1UvsZAtflEf#n>DO++!p^$YH1^oKG^Y3RZ%Pvc>;slBusjVFJg_Ji*GN3-c^f*oB?4Kfv z_ioPl3&87Dco&sY0kEiPU5Dwxl{yBwOfO+AvkD;6z>r)LXZFKu{q||?^vdx7S(W>+ z>YgTEW2ST-go)j{y2mgE&wM=WG+Wkpvu){Ec43(fVA;`VickvMKwcoRi_yk{T&r6< z-%xH;Gp#lO_@4Il?6K&rWiM4lKtW1EFwa6^Jh(;mF#2MHad2E1r`%{)zr@ z%-QJT^Yaf_-u(OSZ2JD`NGY!>-z?Qy8ZEoTnk(^;%n^~ss*$9X&q}H*>+Yc9q!lNC ziaf~6t3>$s($#MT7k*=kd(}K~1IhzE^UEihR(0n+2XrJtk@$MC1$qe0;KQi5et1$X z`vco|+%ae_UA-J4^v+7z3sZDd!HN4hX_(PslNCYyAu@mJDx-J%hTiSSNd1(TPGq-# za}4LgofE5bK18iruzV%Di@zClUZA{PvXnZN3HD>+;{t0avY0PeA(|F3s&%*P1uiU3 zCwf*9>i{eCQGazoKM1g)aq)XJ`C;9dllu3dmi2sU4;5t|41$t*u;6QD7tW$1@ zYwm$+WF*ol3(viIl+B8k=lQkBZ&<~dT-Av-V!E1yZY~W`h&9C=K0xL4I(c8+;*(h! z&p`i5rs&C&=qg@#(+43PROmlJ?$ZU7M`#IkUrPt=CQB1fp|fWj`tZGKr6TY4hhmL< z=#eFCXQwvn&%CBt`LFOZsyapo>_{V2qvO$`zoz@UgxIFR|T)$ zg+a4lF%0!pk4yR5>%=rd&0b*34KcAO0$;6|%X1L?qBh)Fo`9*Zm47dJ2ZHObkz;i| zZ%9*-JL4X*r%*yTd}dMd+%p}NlP~x_YOZr~55>}u$Co%CntF3F(JZkp0n}T6{#2hp zcp}z#E&Bn`WOT(&PWRQDE@N|3&B5)F2)V84IGKbYaUv!zac6ROP{n13fdZtodF*Dv z?X6Bz-?Bc<^X|JB*J32Z+5!Bs5^9V-x-v{ckxw0d64c4B`lJI~f!afrkXR_`|iCQn~; zLv2ugwnD+{hG{~9;WP$Kv;(OhPnN#%<#)bxu4=_aP<3vQJ`d5}(H%6gi=?uD={AW9 zSYWH&xh{D2f{t8x#55pJ{m@;vH!hZ1Abe&u>eY$ePx;4=*shKpdyDp-;tqBG6eX*8 zXbi}xoLuNP1E)}ujuYm#TY z?>TQb?4I0iK|&FTb-&@*J67H8vV!5J^gK+)fT|DcR2{%t!evdI!dqgOFIOeJv8H*@ zy*nVs&-a}=Ndvd5;NfdmR?g2$O>HgtFuh_1`G?%tIkQLOU}eOc$q+L~Xb`;W?-Rur zwwKQLmR}2BSR`_>&_*UZj#M|`&TT_Bn;Q^N5j-pEfT{!XO?eKJpLnfIdFmGkK3HN; zENC2S>eC*dM`L_VG^@2`D7&7bV0UMxx*8F!YsY)9UK z0k|CtN7brgn#puYug6$c3dIr&>7a=+2=^L`+Hum?XOApOCXr(3M9EfJ2cB$#oa2|a zqKtaNpCEt7Qr|T0sxp+#*l0lKaAOL24+Sd(wjSGk?;HhcO4OxZFq!9N324X>h{w4iy%xlYZoG{QO~52Yro=> zcoOW)=IW`lFGEo__lU#QP6x2}qhH27#0k;hsCJ4#S4~tLw z^Bsj`e*#ZibaS3Nqm*@SD(*_iEFj;U(u;c2j5|Yj3}G%lyYtb0B=>*AJavUHC5uHl zqpI+sjzr#~r7QPZ1a4nTl4L|8Hmu!UMLFRY+*!pN;M#HCKcz-0OLTa3mttBnl*E z`^r*Z!es$26anm^j#H%i`Jr@zQBv${9V%-UW%*;dVTNhfZn89icksg=*(8;iTz0!z zYvMv6P3A&TuSG$WF}dd6=#bZo(Op;(F&TSq#>GxZsx58>#^gk%?3Te=Rp&YaP_KVb zLQ|D}2gg$cYRFj-ulfu<$){~#iy|{UvC&RIni_rWZY;Joo|dNsEfsu8l+YYA>#e;g z0@W$Gd$-v&nwqy@*dh_%0gVnTFjwyFk>^Gj!c8Qa#n@y1)pi?Sed{Y_mRPXhUmj zJ3S)W-*dQ+#jOf->Ry~Ci2_Lo6X0ny1J1*QX5SUb1|p|V+jAiMuTbjU*|_sHBc?WA z#8c-$Q?`6L6KCH4IX!V^KzOdqxcNz@+dr58yNruUNgYlnADGt!%rE46cviO?%21r5|GR1c-CP0P9`A__f zliow+XX%qGq6EW5x_&+v8PbgGk!?bOm|2dQb-S0+A4UxFI;O=TcS!qEnw$8dB2Go1 z=cdImg#eMMJ!BCz#l={eaKTbZp@i{*%4wEyI#jC+BbIeA!8#1d!pnY;fvd{P|0P^e%@6k!&M`xl(F#RcBF~|Mdkbv8#0G^KCYX|S!ey=L^BvwN}3lot}ZE)~y zxzwr2KV#gf$%GItn`p1^_d6w7_|^Sj5j|F4Os` zLZGbknPglv*)r3jG|%*RZEnJaNEA59nAJpx48v}H$k_QBPD6`4(Mcb~aX=4SV_4?U zK=(RJgM)c;y#G~Y%1|}TEUT7b6woe>7+!a2(npKU2iwoz_B+c0;YtzU)s7Eee0Aoz z3)$AZ^qD1%<)_I{#Lp)fPu3VjtA*QUSCl%{n-~C^!4@`}RjZSzuOj%AH}LK7BIY%< zreA=_%bydENqZLP>$j7ARP%@dhOQCj%JpSax7lW*Lj!}72JZ~r6cgCk-BC@*=UxGp znQgwd?GIW(BGv7W!;)DfQ9&EgIVeZNc&^KRKRJc049A$ed!0^IIUqj8JGBn;rIGIC0KgpVr=j=@=ju>L>&irtWZMLHH%yufR zE6)=9lH4=U$Iy&=OBOlktP}88mcx!yGD?s@Q|!bE40+XcSE&U4=Z8z9mwyuzSVwov zIPZIpkm}@SVaa9oB5cDI-Rrk+=)QY(`vyz9_e)~c8TrV(hnTwMj_1B z5UZ{ImXrCugvg4`&E8(hKNUUm=jpeJq?bepLiA@h&+$jJd%I;ih!PuJrYLq$+0fMp z9MYaBL*W|#SW#&Qh`DrhSr}3B={>hgs}Q)rICIBSs$C?Gt41=oUiQr5_qFIUcCXsP z9AEQcVvlQMjlkp*9&=8hu{~m)HVjtRO1ng4!Y{m$nq)dKES_BLv%qEeo=U3>vNY~i zjeicTXvxKn-DV2IW z{Uw}Uzgp6G9`(KM+PpBRv5%&OORWY{-+w^!e+`$nN48=1PN9%-poP+K^ zSz6T&>QbB#jl93~7myfS20gJKL)OAnHUuk&?bVT%c5iCQ$oif?F}r+|1}U>RLB2Q_ zUcLlsET^MR{JUpwn0dp@hX)6ZS!3pUHvHF^v4(+h|H`{ku53DeAtr@Nr=2tzo9)Ck z*VYaEl=n_k1#b50ah2>+HsfoiC(mB3`J}+m5#$rg)ic93RyI?g581_ zr$)a(C>_v7vN*6YSs2un8Y(M)11u)Wn8z3ZcX?1$yteZ$+2MmSS+24M{!DVo|KyR) zq4TPoGQfLz1t^M%#FN$R8I^QOb#u_G#k?vX~Jx%IA?@&O_W5XD##zq_Q zH`RNOT(k_cXj57s%>TB9HG&FDIYl0q&M>muzPiM|cJVE>Jnr6y$$>e0k2S7XAk5js z89_f7I3;CG734X6o;YTb8ZB|}56Rn34r@!)`6J-LGFF(x*CWikYBmd4^sOmkBwwn% zJ^a`SAFA8i*XwYHH+ZnAG{S#vS0WQD5-7SjH!XlER=@dpcAnnGP6|E0Q|<)?y~HPt=|1raSucHzfQLW!yd?@|eQwyw;aY-qJ#Bu7J8c zp-ryyYOGP06)h@VGFgxG0KQm;GyduX^HBfx?HiGlWC{?~g^jomN5OUf!$D4HY{q%% ztoA*pav{>-yY??ry%8A=+~mEcF9{jIE|zPFlh-r4_LUlE#U*Y|z5eAO0#L=u1{g-? zKYi`qnxFUO0hvX)P;Q;YrUMqj<&c<+L3my$?`raCtXoA=Y2bEVi+DY3Wk^(7=8v74 zPdr%qDncAfxo{dJOFrk>V~9aCpbbxJ1@`x3HlZI1)FUsRnVE$9t0&#h0KHM1KfaRQRQ8rMImmL-&7V-`$}z zw`xt*E1}b;KcJMd0`BrcMUyvpi>g%wfHnGK1eK|H&MAY8I1Cc?CS=`QpJx!l+NAScAgJ{H0a6qJ+%{Mk^!?P-3fwb?3+6SWl8W)plw0+y!e-+1mArtH zmAMmdDW_TK?7DNBRoZj>VFo*I%l;Nq5~iaowm?|nE!*J3eTyKrws*baed6i%z^QEl zR5eq7u+2MxR0Jz7h1yFljCrjGf0K9C!X65H~OX<#$vu31U0W9%sJMr`8xn z2#^w(uX^p;UeFl)SYr>SP<$S%fDh{srDeIbe#LSbqO*g90qK%~9}uFl8!%F08`*P& zo*#^j@(bZ7yCaOW$Q!-2Gk5-3#7B}wr|v0x`6feh0Kq>N7(Nzu7*NGFUxMOP4-!2K z5ews-$b^Jam=IL&rW!z}5nJpaaHM*nmw>(8Fsca?Wf?|GaZeMTI;PNwVyTJhH45dn!=jUoIHLqa*q9 znh?2-$Qq7w)<6AmzZ4+gg;+D;|1^A^@85>o>s4*DpmXdre z6srR(oM^~f-{3qSX!KR#R}9E9YhGw3%&nm4DEi)mGGI;|tO~;1xB=6lEN+7@_b}Q~ z0`YtXhLK>@a)wbl{K9RgxPMi&GGkpLSWzDrvW@qj>J#yLlbG|0 zx^#*!RXK+#S`z3X zsyoHwvX6E?P0P_PfF@!lQz`1n3o+kIu8aAwjR=2)7b#qp*1lPe6hdJhEcRp&0^@H< znQZ`gRrmkE`i!q5NK1E%*bJD=rV8Jf6v^fv)v{wL&ejHx7 zd>dLEwl_OrP}xr+XNmgaXTC_;Exwgx;ab*mkRkrhY`S_+_(%K!i_g$F`f?&aG={jS z0L5g3!Y}h@?Pk^0R6DDzJ8m0oop*k}PX3!wv*_k!r$ndkN5G>$q-MLGYUDvp=^ot& z=jt=54=_a@S9v`bdXsBs=pP#{?)+on#={RRIZxc#T9vnht(^UliNA-#xNx)NbE=J5 zJmXs#ROxi8t{5vjeC0MN)_Tx`PKv zg<Kvl} zR=v{Xp&B2FNLO$p55=v|h4-d_qiBIb)8q~3J}fet^DYXCGyrV@^=AlX8I$(>KFJ@&$O z3V?LWt;K5}h_a@?c#~eAqVL^Dlw4<KykqxnHFovin)K2WKJ+-pw#Yz0uc%}+$Xs7%!K25$Y^5+=kQsO^u z(O^3O<*x*tepb8%JI^k$r~K6dmpzNfJI0?6e=OWJ|6i>S=1Uts$p4m?ldN9&4G28_ zet%QR;r=E8bX;4oqT>Oc*Y`KFhx{#Y&9<{(8%XLqd|6x21UhtgcaOh6n*9ghQG>zc z0NcqfQzdijORP*VzNH?O*MJPms9rlv2x}07@pZCad3`Ib`61Ia%r=ib2|22SO(I>j z(dRFXBKXW1T>#gFLW9))`D<7xA-~LiZjfzm!`#^1Y}2L(v^K-#Mv&QtUFd6??Q3dw zIknaS98O&L$EDOtE+;cdx|`Fn_NSX7`5*@|0@d^$u%l>p3H{u7{#Jf#3*Pasci8dV zaYHlN3}f#D7$-Sxho94XFb_dM@czUyTKC(TV5jp}?Dt!roSaO#U22qpO0 z`C62l1+wHFTAct-vYxybeS{H+pb&k<9ekFi%*q)Y(Eq%u@*TkcoUa*8wVO(fINHov zqa-D*0RsWNi;uSQm}(JCOT_t0slA-&d|hKzb^p5Bvbfr3X^c*Hv!^9PqzAia4>j=H z%hMC3cGnN>)h4^lwdE>qGrJt`iB6PsF~>RrF7BFs+p$bUMhbNod#%w__|YX{$74G? z6c{$b{JNn6uj702HKK;A>D{0{(w}ZWn&W_(;~nCXs&>Bm47d|2I{p~Vg_>N_T5qtz zP_pzL(CT-fI-9;Gr-c2q0lCK|tX=Nwde_h!WI#=6-xvtP&=H_)n`u>QxgbKLY5+Pq zd!}g9J&t0_0sUifSAssmX;k^@fnwq9cW6mN0-TYbE$&Wsl4+8T`5jRP^^O!~d(22l_)5#diQV@SJE58uf=wbF*XGblD#v z^{Y&fcJb^&f7lECMTBM(2JGi(+qAXHqh(d3+7kNgCMyH4VYJfTTGsyR(p9TVp#$XrvZK~oxK%>m%a zja&HIw@YO)z&Zb+28_#4CNjKL{hbQ99og}>jB@#B> zQX^LtM)Ql|A>@>g#glY9@rq(xa$=!-k5hP4K#XSAW~7bmfTBP$G1kDUhl1HJ5S%#JaEol1cUneoeF))~ z()bD4oJpDKhMXD2m1PM*XI6n}cJoAho+Fr==eAQ25N-rLChGI{$D<=9T87#9iz90| zEm3&igCkSUA(W|6xe#^82s{_@C%oUTJzQFLz|J>7j8W8phB^#MGT7JtEo?D{j_g}L#S;>TIm zF!pP@vc=N2Hop~r;xaAcyZ8bXXhd| zFH}%p^wXzL>{oN`xj`?f!k}POb@6OSjSu=rpC-0doeBDiMJ1f3on^xc7%Q}_zpYYv zfDV$JFvvN9e#0>y)ABe1fw@S~~Xcd&ycdkWbEbZL8(^Q(V96Z%d(9SO}G zt`M!{wU+PC(0foEEqirK=gik-0>US*Zt2$VgKt1MsIEg`ZemlgI$vLcOP|C3fCtFN zd)caM)1wFxan9^WTs~q7wvw$~@kBDc%qHOG#CmrXaeq2&5+(%00b?O5j z_Bg*tU!3ZB*9Y*398c&!y3<~oI_ukLd{hd4yT$x zR*P?%o_ZUUpdH{wf3qTHQQ>>#v+Ep{->{BZ*_HGe_un_)dOt1xpzTdKnwQq(VewC2gu#j$3HffJ(eYhzRmG6 z8{}M08%Y1|qDvk;_!IN6C8Y(ut}|MuBV=ZQazz>N$lj5?pXt!f>5`ql17G@WQ{TmH zTd?5DSxVFYAqLb>9%iP>$>O6;Gz0~0ItnN9R$dE3B)YN$08n-|Yb4decDjW=ABvzI zVSdIUjw(mVTbO_Uia?A^flTnCz4E%Z@k>2qsTIZmlIeS10hG1%srgZpGi&Ck%!G+i zlrna>C}R3GR-gm*%rHisQtHDQRo7vSy)YGrLZIN(CO0jMX$Vdj7E1KD;B^uP&H{x; zlk3!~h7M$sMGql4jHd)Pu&vyNSCrrwX0f3BK$!k2O4ge>6RxF5QDkJK>lEM{KTn{q zdesx}ooIb?^YZDem@!V~ysbH^Rrbuafs{Uoa~@MVh8{3OSYXKtUBy=HTV$2Dh1%*| zk>1wdnuchY2iP?k-p>0z~w^&vG{&%5#Q{@@2TGe8eipl2gT`ylIjPuLJJ>BSE zofBA1J<9#6b!1d&^jcDs@{S!$$KN$~-w~n`I{mbmDzQ**jholQShN~T|~ zF8ziP`MKD$^Jm*ECo+EL<-`&F!t z&DtK+>eIZbudgqC>C*fCCnz(i)j!3EY-}(e+#``8CRmr&(|PqJQ*E1VzX3@Wa8Q{Y^cr|eKYIw; z_eociO>0r8i-pEP`yRCnAV&q{7nLp{WPU&~eQw)?ae_`|rdzfJ?X(jxUC)lGAX+N- zjeYUXVmnqJIC`V)8Lh^Hi@39lqkf&`|_fFRIR@Y1E8W`?r z)#pE=J_a0itfYAg%#aMg;J3Cw80rY=)ESU9*PvNj%={!4w)-Z0DgtEZr3)vrjT)+&xp&2q?&?_Ue5h z!1%!#89dnoj}7u*3G%hhf)bdA){8dKqo|Om%f&q9vDd@Tyk5H;;I*j@sq##*X}gJ3 zT+91s?GJD&E>u%TF&vh|_Fgx%ZD#Wb%d=^Bc)_uVIgiZv@1dXxPDnVMJyC4%p7hLM z55;$V2Gp~i6q|!L-YlhlQT82&!DC9>1{ub*AZ2nN9}fj$#Q2A&zlenwE%+?d7})%rNw@u_=EcT`c0QL;xf@P1|OC;?yssl4a`=jq}~<+(;<3n4Rd@@v3^XYJP3YtwEWTg;%8#ZZ1Dk zcPIc?!gl7fE$JN<=s9(yFM8*uFiM4T)Y^hIIjYbONtMLV5{<0!6hGPc3mT{x<(RIh z)Dy0-?^rnk!1@6+M3ML;2Uvfezm|er9h1LJ3rbY>che^m0xZcf#kZsJ$g0&yQ2qm# z#NeVgUVxhg`01xBR$O`=o>Qq+K*q+KsdJv@zR?pdYj@Y~1z_=UwEEY3_b9q`$k&a{ zqaVFodN~hovPoHY<|gG z^4HVwD*3S=*#TGKXC>eN2E$HsXi7_;2TXM*D=2+>fA7KaEi-r{uquYY4I`XIGY; zV;Gei@=D51vlP~gX3l2&#MFK|Y{d7zzw-a+aNizhvc{n6OQHEisf&N;_jCu#>JJ<{ zJk*fVWUcJWCFCq-=TDuslPS6LxoXCz>eHziVnX@RILkvk`YI+bC+~P385`UAv~9Eg zwVQ42Dg~plb?beduWOz2O?jD8++J!H@lV734i66xh9VTEc&h@Lb-6W{tyr>W`V7u0 zD=Saqn-33k{0SNl?mo)5JW--T(=Jy}5VhLFG=#`2Iw_HP z()(okN#}c^g&ZB^n|@!&sR6!MY?PVVx;f3gx3W;IuT(A2vay=Ij*rzWFjcyfq00Sa zsbAI=CfL zD(Jq1Y69I!^6#}da>Oe0)%ZNQ*6}o^on#BbOYYzl@)sE*ILq5HDP4i05jvc~2k6r~ zPdr`YJd_+~!%|#&0~qc*_@5O`A*8XXFb1sqm`c0uf?wcdRap1iq+VxVAiBB{D8 zmS~!&=xTZ}%KXCB6zi7R*YR4<&E(9DjSmIR(GYxjdNeiX@xzA?gInO8+e%PoO#AMB zsVyWEglrKzT6=kGXi~hLXx%g&ym30@OoH~H4B$sr^{9JY<3DEDLY|#U==1#yb5{(Y z>bytyJTi{$*G--A+;xX|kRKu3V>kYm{^6*E4+kR)^m=tyrXZb{9sRas%DS8!8;fe-y?sjD!QlPx8KgSGdd>b zv6T2R20EbAga;wKWO=qtrO6#NlZ_hSo2d<64V;-7CHsCz#jSeQFHiA|f0JC_BQ-3l~|%^v7y_>mt6UD>-5P&?=SjlSEB3fMAq&NFSv0QZhbw(VNgkLojRdAv81acB$Uw zcR$;z3Nt-@t8UzM_%jcnpwNb^ur0Y8qI9#(V`Smgb~41DP}p)vW0yj;3EQeVR~#G51LS0e-7-c~Yy=ZzK3qYHgTH~#VGlE+cWmpug)o+o zHUPRe9#2`EsB`jL)mpyBS7B`AceHMqcxU`P(>1MJhpRcZpq#d`_xnc?B#WF7j8D~3pB8344A zkB4^$>FS8&4oA1SNETjs`}{_(bj;3an-;+~z;PK~=Cq-bX+E#bz7@3yIg~aLVE_ZmJ=Z5 zs{S2lvR2daa>HM^1tPv_u2QoC1}_SGx{2y~gx%ciKxN=8M-14B9D@NKyo$9Fh1WD@ z+A(VaJ41P#N)C7DvpWw*owsuyWI=jR!l^hDv?if#D5IIDI)Ex_t(gCj9>Qi7;lWEI zBOe4ZePunb&^T&Oc$Zph+%JKRrEJ5`w@P>rV_Za*^qhDba2>~oc?im=5P*t)+!SQ< zEzhzPp%U^2er{4_u1D2_mP6}8 zK_6@Yj|qVZ{fgr#va^rC&O?o__z{$bUd%ZVQom@!U#UVpBs7&cSaF9#e@LDe16! z&T^AFQQscvPscBre^*ut8|e@0xwyaIEiTK?PJpu`JylQM^4^wD5xO;)Z27oqv7 zf1GlDksGA{88}x)p~m@lVVR_wA3!bw%)vMR0HUV!8dUA5sPHJJKb&*Tt%IRts>^(D zlPUOpjr)@w4+XmNQv9bsxLx0Pf$KYurCjey^Yo@E#Sb9*nxO$upZ;-Q;1L9N*{w8p zaRQw)`2%qRL-akL$(c{aI0}afvT&2Y_?vZwWB;a}x+ub|daj5F&7iCZSkp0KL~;lR zNc(d44m1Jkj`!~1{y8r4Z-f{cfhO1yW&|&Gs5;lH&dst)f3_a>@sT(?KL7rtN^#M*Ubs_xb>@HW5(IE9pG``|dLY6O zk4C6~tqRxvZ@-<>RR;6|w$?O;uNNBOapd5x!CJUxOft?y$so=@(K_B|V7FuQ=FP`n zlIY)Jsp^GSx&EA%z`Vtc!Rs0x9I-Wt?HnZps^><}5DcB&MP+5$CNT5YAkPrPi7?)<4DKb-PH>a*kd0-;o`s9? zBpjQQIcNMG8wE1JwUQz@MugsI9Jr)4g=#g2G1TK8JkyJ6Zj`%Xe;$C~jRZl)sK=(? zWRNqX@PcqiH=o#y4+6(^#CW}r1T8(~y=gpoMSV-VdS7Ah;;>@c%N$aQioPY4Rm-?h z{}iNzFd8VkM+DCFYHC|NJ++RHV`i#XQ(y;7qR#Ilc}dUo#2QdTWf*^=gp)?X$a5#2 zgE$ZfutCPc#O#j{NjIAWPAQS`dg)zso*!|As8uEkWhK`?<>W{lR4g1-&stlG_q?f5 zT}MZ!;t&TKE{X1Q1Qgg}6FIV9VB*}$Es2I~J!|B)j_#XM)8*qF+ircmpRA(=_wZ){S{H?h|W_r;~W1vaq=YPcnFIAr_d`GR~ zXW6RmR$$kDB(Sc1K)&qK^5&H%6!xuZE#c{j9tWV&f)-H!LOX#9RxV3yw7zS2Xy`9} z0PvC66W*GEHe7e_z*8J8u+MGHi6P&n>3yU|!-^=ia`h^-qb}{~A3no`IP&cZz;$&x#up$XyVv&${|;LY-&@&L(;GnV z87gR)ti0ZZbM88(6iBHhhE`#nBWhXWzs@L2?CG%+)K-CuYr-1fc_pund}%h$u2B8D zvS9hR?9jr^DW!;@tNS|_8l$hP@;de!TzlH`GBTe!+5hu#-(=t0&-MQ@?E5|Z+i_R5 z&LIPF)PS-ldU@|&NRfV82P`r?Hdef+XmsmV-Q%WgJfS_^IeB!tv#!JZ_;Q8%5ESLQP-a!NqE8Zbu zc5r~kAX#%_ZFR)6e+$POWXFp62Iq(7l&n&6@lY5pXaAz38?5*xG+4D-S~{7Rdw&rlZLMZZ_RF74y}`(yDA4C4;;!NUh{E@sa$UT_?nRzGP61r0V>8 z$#0SU3DsK1WW1yC?i|dhs(j-wg27fT2=>nw^!R+1hYjSpzf}G2K(Clabg0Dsri~>^)B0 z!M0W@0V9{NBoQpM8X&&2v#s?^E!=X|`5CDE%OOiRk!t zv%}x3eHkMe68^y}{NGO7kBE$$43af-bLhh>28&c?jb~DdSTm!u&#F{c_Jfdd-eK^H z9nS3y;MVjG4xv~QOo?F2OLY_hQaM3EnFkd;4Z^k_k|P2(5;}0AVnb8v3zbv!b?efx z8Pn%-4~)By*+8Wapv!oNAfC-c8>(bnz#ybf$h(r`=!`m>a_nHXU`9#>HOxggfh}M3 z)YkuovQ`7UN*_!35&eU0Ro-F6>qSm=6>E-ub~%|?YBxK?c)G~4UVT>s!0JEt2UuJy zsF|ov&Gs0zo9R(;h7f_B4#L)2eCQS|UvngNUCo>MYmpNzlxZ3F@HOlwHtj`4h>O}= zOq-Uh_q2jginLNPGMnA@ONcMLYVM@R{{|gf+~0I`o6YW4txl*?2{si6{FNr#385_z3d6f#G}m{~y?MbS3}YLo|NH|4Jq6(~S_%JqESH z3#f2C5d~pFc?UfX-70EUy*^P)od=@0Dsg(RCsaH3T^@zD-Nlny$?$|VGUNk4B;%i8 z?9HVis35WccgJQ=cFQH}$Pjq3Jo85XQ*NVcG^S-I2)dO#%79kiUC37(LT5sa5mn+QSJyt;ag-IU%pYh*A48wR_x;sS2~oHx zA==|5qE3b*0&XQEXqexxv;A?$^gF5^=B=Nv>D};4 zO4)=~sT@uCq>cPrQ-r*;|D;tE2M*%jn$K}nLXh1&mNRFQgk4Rxc!a~~?3a>eIT#6? z1O0Qe{m6W4mI$Ye8+&##oBU0&@V@RJiiH?MGu$a{l^T}t#H)hGW!I~XA0G}A_;N*s zi~1Y1RnMXBCbeNpG=BB#{QY>bf0i=nhFyjmMKC?tJ1GYr`pey0@gM(LzK|*vhgs~| z=lb*YScLuhdvn~z-4{IJc=PZC6u>Zs%$Z{>aHXUdn;?@bymge|^u;mkhF@O?+hBqv z^D2gU?F0UjD~F6Z%+FuqKQ6IvN9_Konkx2W#|9x~-{Kxcoy3TJ^?LPb6Hg1BF3MiK zXq}b3BRKf_5NB0VpY)(5lWvU=n)`!+{$XnVI@Q->rG(V0E~T(#H)dp*21ti-^<9(l zZ8muKP%Qaky7t@l9{tM86fMHwr}34<n*~N~=b7)GA$_wv~kBTBH;uW;|Bgjnia{ zN`_KcsZnO!3*AT-6Ezn`%E&Dvw;7W$zWe&9$NqkPJ^lgn_}sql&+GMky+7CLUwiCn zvToRq_SE-ki#VHU_i1}-Tv^llgFGY)Ku;!Ws$9qB`GnWGZ;|TDA8e3U`Nx$qySl25 z`$eyk&-r{AKWZ@iK$8CuQ_FO%ezb7AQ>om1Th~$%);FKn*5#TJSB2|Rh*SjVs(=p} zkqx^)T$UD@QN0z_+9rCY5--2mJfUcG4B+8}@1}HL=Usu@zF_nb+co-d3*pRgR7d@6tt(*tQ4we=i49Lf0G^Z8fTogFhkWYwz)~w&C z+AvOtJ@j5Sd174tA?*rS11s()1yxxqg7zIC1}vIG|HUr+^k@^=A!(JC%pujv_YtF- z0L=LYaz_+N>yK9mk4Ng39gdEU#h;U-mRx|T@;tX#BX5)NTDPX_W1o4{SrwekvH)X8 z;vKMah#qeQzc&i4dR_o*K&u8r!tj0N6&g}2`FJwu8s7O^jE9%n_TowT^|*x19l456 z>?zjd($g^R7fCJ1`C;5_4C0;}hNrCs(ECrIxNpj;SQ3TnEx9-Qb)tpVs>-y7fCO-M zJ`I?!p1Vz{7ctb!13tPr=Q>>S%oFIcAsfR`t;KD&diB6_#O!1^9yix#WMnX27ue?a zew)Qza46npcYk|BBf{m1AyGc@_rJt3j7vRCLmE=PvtsKRTDw|}b7{Twj!F51N{)kKpf`#SSrp>R5Cv66*FmekQ7^~XoF2u4S8HrtME zu0m{~_9zr43+NQWA_L0j2*zSBX;{_6rd8}CkedsiQT&De=F}^W)r`Wq>!Y>vc_3~1 zQled`cT(>sfAF4775YGKJmm?6oQ#AxayDhp> zshj@f-rdcieCBjF<9SdkK4~`e??QiD0H*U`Cerk)Kk*NHY}~t}n2|X5a+3 zKzeu3BVHX%qP;cwO991yaj3eg(8Zs@v?>ikcg8&x>Q|G^pllb7Sj2*qe(R;nZ0p-ayo z$%Zfhpm&p^+LtALae&Tcei54b37{Rx@QM~RJA<0Q17*t%L)&iUAvPfcH|+lUB#+VJ zRkj8p4T~^UH=#;M5KCi>9rcjJL8cg@$xwHU8c#8#1$nc6AMV&)Tu|FJs-$Z_I_&$J zv_U~k^;Eh?lVTXI)nxKP)G5me7CL#Sv<4_`s@CP+b7+y-$S@^ze_xX;P^ns*Y}^Cx zGU&J2okiN>J$VoLT!uDA=Rra|pB*Bd#2N@;iRLK$xSg$gg5^XGvw{7w zz{K?Q9U>v+s2@=eR`Z`w!De-CMoCyZ*{dQF(3$gui&Aw8>q`F+ZA8mhcjR=p9=)BM zR)caP{JLPGTs%EJ8*oPw-*gBLQyvC(k?9Q7=k-C))>q0#9A0+n;r68A1#&DFrZ8DC zO5EUC-TJieIO>q&2zI774#1BPA`g+#CIL~{$oGe8cb9Y?jjGCjT&?PSl!Eu~(z^~1 zw;Zmq7zt<5zkt+!iw?1=Tqv8z5oYe1@b;jyhzvHLzOabvBMhc|bjef$mi0I9EuVOs znv}k=95(FLHHyo+TSPDb2GzeQ@?M7Cc_S2-qM+K_%XA5cRH{}lh~SDs9DeWR5) zk+bNNaSdM2TwkPRX{^sZNq!s+=w$E(5dAnIkQLWsLlC|UB1h}`z4i`UAQ8NA(tCPu zMH6nH9cSlN9$5W`XLx~Nskp!Ke|(Ih1xH0^Jpaje3@E~rNMHOE^`%O?*h>5CY!-|h zhYA?YSw61HjUSNhi+jYzS-hF`d)1lcl`ZKDQ9F&P2@IK^5V$TrQzv4-q)!EVz_gAD z`|8d|&{t6{ooZxioQ@4$w2|pMu2iD2&sHO)YGtc5F;vSo|MlomVuerS+9D9FbDBVe zZW;JMAi8q(4sqhIq0SZBMZ|UrH7cexpVJuq0g|XYg4^ANjT)|6Pe8I?ang6Nzldq= z0c(xu@s{GNinfF0-eDfW%?b66W|YYaGWv%8<}asf8QU&9WPMC3gfJjJAR$oWj$ONA zwVy6F`D4~oj8!MQEG6_3^+1!Oa2U?3R>>9i3A`z*b+t@(f8DL-;_!dqwk7m)4Ac$63Y>yf5Q7OOC4iC^3i3ODO+|Rqg1xW+PJ*n;G`6hO>vbC7o=Z zvwDC<4-8uWJ-5usn-d#H5Pd1YVg$2hrg4JfPw5FxlOvPJ&&hJS$B2%JLp8spx^`Q~ z%%tTy2mSmuCGrA0jfT6=_TOT+BnF-8$cFlmwp3Dle+y?-cC$jOgu7LLyqoB@Zq9Y9 z87I+|{oFa+fXyxRuvf9A@_2=r5~7Y2aPhB_jMA5h10}NqWdp zBU%`?e_BMTrfnm`Z(_7(wIG9Au<-thf$NXRuCmrZP>CHsm5r~P>PGRG8*d!Dq4Ki} z3JM0piF7=|EZ|c_oYIiIeaNkBtHA$WE=H1zwn+@_NcU7UY?+zEnI!B8vzH z9%#e6KwJ^}4o^{hPso}w`}v&y^J`>;7c0%gN1|VgjYz%C*J@tX!)7I%IXBFpTj}Qr zR=ibRWqr9iMaSzS&-f&XANn9;)?twt-g|80r{e%8%D|2`5qv;Y5BHMD<2BvMkeq{u z21Uh6&%c!f*poR({yGP@#u%4u%sQEj<1{}H5Js(0Fz=la%+fFeupPc#CAe~R3Ld{2 z9-AORi!~YifS^XL96)9hdO*;YGKQWpo+H#+!^=ZBkbE0U{c{_X=NYoh{FeBY80cE_ z+X!lS>kM^)>KoKxKfpXOE>bsqPPNwNO+1XgUq=ufO1;#LeH;)IitCKY>?Fk=(u6^V zCB4{aL1Y-iUHCo|USK3_g82y0KdS_%Ve5)7o2A9Mxh z4vDL(!@GifeJbyj8Bh^cGfL)f)F0n#ueUCOi#Q(zY4O@|NHc8j1g_bJQ>VP5UfupMX0^q)_-%?x&87d#nFr% diff --git a/docs/benchmarks/dart/mediacontentlist.png b/docs/benchmarks/dart/mediacontentlist.png index a0d404e06f4f1050a271e6c81ae4cd5d9ef3b2ce..02f785e428581979341d1abbbbb20438592889c5 100644 GIT binary patch literal 53715 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 QGS2PW9R8BE#pBdJ08R}nvH$=8 literal 55357 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%0A1q?grUXK!X_ePDgl6ICHGobm*f(}$OIDj+ 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 AJpcdz diff --git a/docs/benchmarks/dart/sample.png b/docs/benchmarks/dart/sample.png index 11f2d7743e7be6037e3f483d333ee632d81d44e5..f274160b4483a0901752d89795a7b92065a8c5bf 100644 GIT binary patch literal 46415 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

EIgca6Pfy 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 diff --git a/docs/benchmarks/dart/samplelist.png b/docs/benchmarks/dart/samplelist.png index a4b8cb1c09baa8c3bc92d7dfc2759dbd05d83543..72e6cf7b061bb6617204bceda460315981a4867c 100644 GIT binary patch literal 45351 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

ZFS;E+27P`Rk^t+hF3dO^$5EUse&j~ys0 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 K?pTFGXZ{ywa!d&T literal 51598 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

%im%$+Q^!qi#jR0IkseudN`3+Kzs zm(LO%7;CeYXIci{BXiKL6Fxp?100QqEF@qp62XtyAX5k>gvyM1m|#_udE&Tk(Uu}q zm>75lw$@x641!hb`Q<#$lX8N!>n7SMk6)+F@*tyJ(1{0Y)WsITJsXX%q+TzEUi{2U zh<{wT9wT-t;B!67X|@pz*6C5S>z+h(P}pgLkB?E9;S3`k=*Cj_HUsU)*Vamn1W%qU zB+RHN>XBQHrvE8~Ho{LdhY!<})MQ=x$7=Abm^ zW`Ji&c&rJi2Vbtb2h-SRpYeEpZB^7KxcBa+lX9_SAx|7LvCU-R`C?=aAtZ`jVi?I# zR6S=~k}wXT223IzG0es?noUD6_oDsr(-za{Q_P&2@nV=ROQI*{P2;8h;E=__eDTFw zZk7O6_G{w5ROyAgnH3*#X{NyBPpDkQ*I~&RyZPafrE6=Pu`#n%7cAuG3C>v zyt+fKvD`6mFS>&n*!qzkmIv5A>`sqs7Z0$t;?KQ1-3?Ca(oAP?EG>Q7P$24;=r@GMCJJe`9Y-RFbEz26Y9-d(}Yu)s+HmX}wz~f!c zxQL8iRlHe^^4bv_lk}dDSBKvnYw0fY+tJ*9GLFkyA!MIfu7~!#+`C8LoeC@OIanX8 zn$;8|cd$9NrR|kzejENj&nYkVU4MyelucOVxa6w(RdNoMg99-)6CKuS+%_@}QALZXrtU#`)&%IAuC@CDr& z2Q+c(;$6u(b(RkaGLCoeN27}PX@)l>9(L5iFy6Y*ut=&LCDGO1tG%>zi+IVec5jUR z-O+zW*>rc4k$_k|PsC#!NrS)*r}HMJz|FCgFa^pAr?}>3`wraqG3P`=_t-<=rnZPC zJ*z!faHn#5?1egUOO4G*9=0wx;rJaVje_uu%@8qlF8K>D>21j@zdqI0QubDFI+C| z-AbqvrOC#C?>oT0i_zJux6jbdI%|{h@%KHs#2n$s;-ay7 zhIO+r62yb|a_<}x&AGRc=RFY#MqdXZ@)xdZy71GLofNeKp{k%x4uXnBppaIxw}a1= z{1lug;sE3FAiV0VG%g%5X;>GoSrWI27(0?{t?k#b>dYmCi~$}~NQcH8c%Y!(B8G8f zB(|y>zxEB>@m0IE%&$;g6=P`Gsb6R0P)({fkHYR2B|Zdzrm9#E)H;^}k0_Ca5Z`26 zSy8V<*z<E*W+VQq%vPxzkNI7f9`+xu3l=oLl_tnj|2H69Urft%Xe@x+#tYZ`;t+t70ElZ=$xy;-rpXsb5MLvysAYG&# zFk|3OOLH+|%-ZAp0T9sT+2n6^x}b0az{JKGemH!KS!dmn+}lFqB>OKK)ZCYT2)>5}Z; zkS-c`??Em{u$m`QNeNvnZ)= zL!%&;F#y_P+_|ycJnhWz#>thVUPf*3T}U=)U0g7dYM9$E@44iAlqaj`vh64AGVjsus7M*!s^g=w!mXwYqNMUIeZO9~@ zEwcnhAQRpG!q##T8+Z&SGrU({12jCchHsrW>HHi)>&CQ-EM(sl+NkbifgneWsVT^z=!i=r!w(QP*ha- zW~E9)dw)0GxXQSHji84I1&v@NnZ;oJ#8|ypyh*Ub7htUK?_iubf=XQkz)vu6J?=Iz z*v>aBrZGhv5?(%tf8n~t^fuGm5j${~SAESmA~{GC?&j?}TXpbQBsiJii^}S1ZOgYe zBayb?=PS=$g6+h{r}~esC^e>8HFQ0$$nU=S<-EY?GTp1`AAw)(Bun|jnsKbgdcz=8 z7q>-yiR+YApD(P0_-UsHpM8DBl3+YoD?A)m@HhnyL^c^X^(uA_l zjFAIjwEun%W}y^p71mf#FpyV#lZ1#=NoxTGIJ95fc~5vwk3&kv&93rEt<9CfMc8Pckdo;> zZ@BSO?_bzDfL|x5HSj5kpdj}4&h@K+l9xvFc|daD7dY*4V!ZY&w{C7prS-gW1~e(c zfAbTQ0`zC)QBw>L58!#+rV#P&h10Bb8hGwH;q*D!xUi5F0pj`H?#JMI_Um3ahxGsO z>?^hHCT;id5&3oduQV=}Lv_eN6}IbILV@X1YD42nu)WG=h=f+d8=w+|yn(3Z3t<`; z#-cA+GY>%54&Rs`Ua0-B$oF71Pz{y3!thS?_4|6K77W}&z4l0V>|&Sa4#o2YFM10N zgSG6n)Z&+2;yRZi8TE1Gv*^hS?vg3TS7*<1fw$^}X8qw~aaB_okYf1?_F!1$w`51nbYXuuiFZ}uz3cuj>EI4ycriha!CIx6}7ZcO}RdS}CV!r4l zquDio?_zy_B-o#*0Qb78Bz*xUsW=F&(q8U@OM@|>7`mHGqi)(`B7cMWl+up1kW*are{%#B~Jm==jB( zUV37<&fp4n=J&q0aj>wvoYhM5^XR$c7$JxXI~XdpR{hy$Py(ODNmc^;#NuLj%nCpL2lZBt*i zY*d5q{l@X0Lj5Kn2uHG%t=2IpRN{L~Q!F|f2Hw^QW>Ax{iE6F2Z`3~fmy;%d^lGy6 z(fPRj1?9Rkh9!CR*kP25@M(if*6`P*27&ZIsZLfbrCBO#>* zW`EA)(>wB!_dueV zeQ=?4sRhz&w-iAj;Va1EJ&dV&I)jokZ4~3C3TJ-&os*M_DF#-TQ=s2krI%di3jk&3 z)L0q1*CQN17q}$h2Z6C84YF``>|d_eH0CLE(Pha5YOA>mQUUnw$Z9>*{xIpe0f!_tdFa&Lse!B7dWh@)(C;6+&ue@GUM6--ipTDYWP!FW9m|v6y_5KNKfkw|>!^p!2V`p#+X{)uS!;s_& zuU3=7=j63<2WWa)&o84#0jA(QzMs5@Si*;Yn7=3>BNeLBmTm`4a{3fQzUi$*(JTf+$0)s7` z9Qoq7DCJcT>g)}De;m16`&KMl_apQ|G!K$ofbMP35$0Q>mh}YCOW|Y5HFIeSAjSy= z@Bad;>KdIK_)#rs77vZiwoeh?!Dsag`Q^xZ&lo1q7h3h(MS0Ys;0}+nJcQ~q4~R`)GRf&Vc#$mzn=(dc zNWM6Qtl^G!K8}x!A^nxARwDhPg)kDwf~}vO?2-aN!Kr?0*|orJ zQZ|S?5+b60D2Z{wG{k;d<#UUmwGU^ps3|`>0{hFse-Y!r#(dlJ2MrKc#g`k!b^)) zCH)u`)$5Uy6EdWcq-L$AJ9kEdcntc7s0sU5I7kwXJ` z3$eqV)$&*8zd8Zn*`(&xB@iI~wb)p)CZUhD021_F^6^f;_{8{mYyl6ZizpcBlid9k z>Wfs&sv)@$nYiV_V4Uj5@!Av>`jblQUKbq7y79DrnDAH@Pd^`h-39x0<~UwfTd`F>NqB%t}4 zWfFieC9(&RrSJ>!G1QpZSRH?@Re3+sHlUA`ioMny@#2}KCKN zH%a^M`F6R@Y?IyNdj<#sE1fS9=T-%|o_c6HkgpPj3OaD!#9>Qi$y^H$ZFRAw2EH!-h(b10NJ4Z|6 z(-oc6;*@H%Zf%d&Ri3YXvdCHYCWmSD*d6^K2~WSEiHWz57pGJf)_0a4DKB_+`>=JD ztbTR$0@;MVeZq&#Yv=PW;ylvP80;7}+O8f}9~G&Xc+1yD$j3UMaH!m=D62oQ`Adaky_V%MGAD6tgz<7OjIci+O_uJzlXz9-U3DSN+j ze{4&as-o^dc7p7i-}iDA`7Cdq!_Hz2qstnaYt>vj{PKJ@MXKlLw zXn(xc@X-(5WyXSG=})CTHH~~cmJ~ZG+o4+2ck-BC@fo$g!?(|oG1W8@1`3l*2*FjD zV-HbgAKQps4Q?0>L#@XniIfp~c)CT! zd<^u6in#+e=v7GG*3vL+K7cRArhjQ|zDd_Ack{6){83t@Igt(f4N}z%+eW^&sN!c3 znM|OLB=9QLomzKGh~kI**((j@#r|9{tp2gXSEQP5^Tm%vh2u6&EADIyzKtI2Rej=q*|N}r4` zK_299Q*$z2qKQ_|pkZR9a*XMr$*22zrH#p+$%Z~bh98bB#@kZ^u!v|FC>F`;`&0P5HJq>Ri zh^5Hix=!%H`cBmf_}~2G@6lW;_v0yL2+<^30t(BQL(-)3taUD06Xgb%vIhjI8%T^m zPDhGAJr+a*4rk5dy*^A-ECnueYAK=brlfejJ73`x+chF)UL!&Vf;G!76<+9_LvG7v z>&c>LckF?ynQ`Y%wW0&ors1kR`gsdBocuS%bCLOn)Mnd{XQqbdO54f%n2t@}xl|+f zT%bU`|7-rsRTqxH-~|*`O)yTp1+JpGv{opY_Be2C5~E5&qe^29xNub{-)SW2RiBT6Yw~<9$Il4Ok5^QL*VwvjHBi&lO-{IRXQ0;=>s6oxB+TT<5pojp~qL0 z9X$Ck@+hNcV~V7vvYU#0+qT=}$SxpgeJ)nr&15jVvg+&m1JFyqC`O4!wD zt;YAkhBwhWhXfBztianZdNb#b$LmHg7RLo6W<1g-`14-ff2Q z{aZd>RmdePV?Uy#EM)KV=!}2fA-9|BLN^~f>TwHMlGSWg-0*ZYe&Y!m6Hiuv>YQ<# z(hfYb-Nob`fJ(zKh%zE%#$g~_`$%7OzBH0Z%6hLC>W}yMTY{&ETCR$i-ZgyGr`pb; z`XO2k?55H-Z9b2CP^U=^NWqp^)I%Do4H{_LXir3GP z<%39rCce=H=@7jW{gHkK!a`J@QCJDx@vJhk^qTF7bBh)u=QEXNWEA3+O%=1^c^x+T zaTNoQGzYB|3S=qga}(7WrNAY0cB%WO)KfsqSKrrEl3*Z>ton$$n_WEFbqQQ`KaWtq z#H)=2P^wL5eiTgrR%TNK1Z#OZjhdurZxfy}Z23Dj@C34w7?0pujXTX|kWG-pC>M?} zzbjmxD=zzblciu#ZlZa@ja;onW7Ik6VJYu(^6p*1O5~%f?qw-@JDgdK%OYQrPcc}WMWx6o*ZT5%^@dp zx%FtZ%b2En@S$LCQT~%4T@2v2xRe)8 zkFQ;jO_Y`8u@ZM&D}3fRvQ4XAj0L0-7e~@FiVPcV6qwlA2E1ZdfDNerVcWhnGq0q` zaV-y3jX(W1*+79g%a%m<9!nB{RJikz1m>^zeVh2IHy$TO>@sPJOq;?J^vWu3DK{Z9bh?ucEN*rA9-NGtk#hODk~=N0rFse z9E=M|OvSl#JrqThcfpXG=#{${yU?ZETc4BNo3{kn2uoKDm!8SKR(a<(&rDh5Fi^wH zpy+S8;nzMY$9eJ;@Sl?@7E7`*m`KArq3sPR@iDT=zj)!xdJ->8I%!ohIq zF!0LRX|WFifeZFnPAZ(5H|gW+cZV`9RQF<1lLpuH>5zf!3PO;GwWD-wCKvSrMS}Os zK7eSq#~Q{{bj@Lc8aSC!LJ3G{I0Pu4kz_pYIUk~eEq|pI3E*NM1q7C5r|dIdQqSid z7C(!nrt;~R4nVV-Rl|#fw3){d!8C|{9UORx3MUt^c*v$C{op;x;o;$0eGsTU23p|m zBRjl$sS$`i=41!bs%l7TeU*%K@|AWT4H8N8i7?vL-Ic7Bz;k~B6z)PAkHq4;eTKoB z25C{z)U_tC0zcQ5fZdzG7BT-)Fss4uWD1s7#8 zxa#9K(|pAxx&&-%1cA;FF3DNw`M^uTtPj~fk1*I1z3hpd7LA(ECDIM`&w_^~IR}YI zJRvZOlz2xamaLONHR@33L(WI>s_+$^JKZhv!W7-nr-M~b_y1aGnG9Nv3@m6#gJ30V7JJn=&+%uGM6qw+7Ig>s`~tnb#xRrTK_8QE;24?+xIE&8qs zoR;}lRiIE+)BiFpqL$)En9&wtg5eL8v?zl6tix&Xx5x71bhopjAl2L~bV5ryjln%v8l;S1A zpfquNUXR&l@=`rieDY_ioorgRPBHjlQndbP12@e(+o!)c;FXA`BoENC6UfxldUGG( z3I)oGr5=xOty6iJF^=^;58b#@o~z%d%nfZuL3jIw|0?O41I$8)OoWu!NR+}-vNVyx zm6J!0k{WPTa}W$ZlBk!pS!fApiPpJ%3b~RT1}m4nNPVO7ux6!~#RhclT$M-nY(CQ& zY)uXp1!V8$!UB3PJzr{J#%B)Kkp>J~ z|EpboXGm_~*^+zxCZm;}1_McQdwu*jwegBqpcxKKa9ky(HE{6kqts8YrAF#Z^WQ2T zD-TSL&^VtQfAId>=2XWjiDtDV`_}zcQWnFTQ?yu6IJKcL}^6prSTZLb=igfK7I7eqVC&L3IJ@^tydnHK0~?esp1^b;WFs{PB~N~G z%3Kw&)2)tfS5o6bC27Yy+)J})30Y-|3qdTC7lr)gr_$e`DpZ+a5byVlO|%vbW2zAQ zrobUF%LB)Pt+uoN+(=mcAxZrYXdd$Lt&2#l5HC(8G}SB2G$N$(YI6&aSDDc)H(^iK zPK_W;zks#5d=j^>UZHwW5?XxamhG5dC)ki);`?qIOKgO0>q+a_c-_|KXEOZC>KTP= z@BLaHTK8N199P!f4|8N<`|6ttLf_1!Q)3DP1!(a2J8)d7I+(v*I+lZX&&ghJRRl6F zw&_M&v50Fr(YuwJ`b+8YrEmO6R+DY^9*4v-a8NFpwZK{nN`6vSZ)cV8!E;hxRFshZ z1lXybd1vG@tsH)fCZZvG;kotJlu+H1d+UpS*31)t`UHRSAH658n<=>U$`?hLr z7oG0dnR&9PzVT}C0oZW<=+P^WQjZ?6c%|4al-v2~+?2#g3L+1>gx%d%)M3Q*ns^k7 zT0tbw?i11wybi*E6mAp5i<#&>mOOGi zcp`}kygFD^UgI>(Me%khgE9!&D8ii@9hx`jz4RpPf|FA*;CfKWa7LZUpy+oWh=Y0z zE$CY09L#r0e9#)3+Xu>$O~He0m#-%Y#L2M3NGCTniGT|!vv%wy#>J77jwB6ywlxNS z5hosD&;5}SOG;rsW*XZBpYU`Fp_+iac$Ioz{_|xMP&sirOfibAhkPjyj${Qa7^VV= zcySWHQ}i)Tmoh6a;OYmNW1flOrCtZ2HuB&k$cs}R>@IrxQrm8?!d8@6XA=qN9Ga{D zSB2sz4S+R;B)s}|W5Jh=r4cm{9-LO9I4}w=8Jm2*%Uq(Dt!p4|l+pzWQfGb79PlG> zm~EUcqbii?f^CZAJ5;fw%<9O)A{rb8A1uGK1^R>cN;j{ykfX#B+As+0UQDq+C^^DH z*ZP0&<2|H9B&`giQ9)#&E6qa(QRaAIMZUrAF!*y)_oMn$bw9#3yHT`(&keg>cw#@Y zf9OkH>GC+R6>1WPUtRr}IW8dc#bmk)!N~U3K4`U-dP*eSN#O zOOUE@hl<>e&TiM(Tp7K9%))80UbEtcWI_o;r?3CZ`uYF=HUE3`MPxs2wUrck#K6E# zHlSr3=tJ2FVIW$j7D@x)^s=f!z528Q6L;w95m;OL;B0s8!`^I6wt1PoZHx+(Flc`TKXVY zz#O&7ru+E0q$ixDE12@;bb=Wv>{%L(2Kh&QYxdBu$)#VTykNRpIt!WN^SdA^1V05U%F4I4+qy_3aBO1DZpkaCop-ALe$}0{i0#|9nZg{=8!VnU z)D&!5!5U;*S<-nDbJLYJM+MsCHU+-P@!BMQk~K{{Y2r@KWNFbi;uKhxX_)Ack`p8z zNzR6nz!tMTBS#X;TY|+;yl_(5GNP?_Gbb!TS2JV3bWhXCkwXu|mmP8Om#^U&8SZG@ zELAF1tNd}~VVaYv%oZG{eJ_8e(h1T`jzdLjbo|aQvTEEfC$A8S}luvVZ+Vp{j_xh`gWb~rH zI!I4hUMDmjULf%5MNnR3$Gyl7<9AV?m;5Z%VPjb;aea5J`ji~LO@hb60*zOHvQhFK zaMA`ytzBR*?G#ot(c3a$GD~kDd+e5hS<&77iaJqeo=SDRc^0--E&gV;ZOvlarZnks z-O!==%dY!7JQ8{&-q{^H>J;bjD7M=v_HI`I3XCy_5Bxq$v6QzQDlx_DTYBF#5y+Cs z5M(^fZ>fBZGzLI&|7<+snKdoD4p;lf1o!oFC_81OPg4(Ykgky(XJ?6a)~Snde6dZd zBn^@qA%Bd4#kzAs5&F+{$BrT*)sbqg6EEYt$|8%EI=Hbk>q`Gz=M?IWgt*@e44i?- z-%E#vbGZ=JU30RqHV+@lU@p;fuE~Mk3$@t)0M3KY|3~0l<6;(VymjD9Sy;FtY~A_X zb!#a_8FGGJR1W_Nxn4}rcv0t%cumHW2ZP=wT)eF5BdgncaP2Oni7#IO=53-3LYgDV zjKw4%l6*?WXKYd zAZHd(y-Fjg+zHn7eJ8|XdyXd>C0OEa4^udzc2Asif&p^PRRT=Phrm%q-+eff#kWX? zf2%WoB{ zU$&;Dfh9^chvUuo%nX*I7Zds{A0~FP#LgNx(_S>)ZEM;_B-V+2eM~Nc@hqIP+@@pWn0pF|)w|mx%#ZsieDJ6Po$iIY3P?Q(AB5Hj|Y< z!x>;y?4-jI-HN@cwtnDDUFq0Tb@?end^N0xOEg=^9fy7)F@oLglNfLC!+n{%*mU6;)kehGi5aJU`29P0OsFsZ%z z#WVzriq>sVrLfAPfA83wl+b4cA&u#$42w4cK6D4q0fRiW)`0rSZqb zB<;&uqm$b3v+4o6*NWU0XX>ZK{*LkE` z&Sj~wqQ`F6*%f{k!@f8VoW7CyJJ8CU{OVT3?lLnGS!Ww5^PE&OnqlC0OG-0s4ojVF z$h)dGTlzL9>qA(e&+p^8a|WO6vr>y zuq4M95c=1Y&ns>BMakjb;=`H?lH4=nF=&_kwv@aP(~!6!v98Ygg@sI#q?<|bOA-H@ z3M$iBJ~i@l94@qW8JIV4MoogA3-KBFKv*xG{OI=;OFz?6=}8YwZ!7P?{lrd6>+f@T z?#I1kQKuOXiy(RPOB<&|A8lJC@aX<@rov{+`wWY>8KTXi8%GF}ZaL3qEQ4oc1|gD? zMT6!pbg?CGZIN+TyN1ng0q4?0u zD=w7c2?j`v#)vY)CPt5)O?)!*ZX|Xz(bnjEvi9)k33K-g` z){fG0@clrxzu=4{xS2suD`!~Xuwc+|5=g#SQKLVx4b!B-L8ox@VOA&qJsDM1}gPn;TBX;>K((z|Fr1W%R5E(no5+N4j%x9Ys8q|jiVIo8})e4I> zYDk#G3yH|D5^L{eGIbOfFfp7ou{+|hxqxShJq|T_6vnDtcB7|1_lH)4_2*iNSwm1_ zr&rWZW0zd^2=hct>|RqQE;PdvZ?0%AW?9`;&sv9KA4f*L6P8`&WmfrRT~DPV$y;a% zds39duSSu~`<|u>HL*WJB0+lFVE4SnXjI%(gd~LPO;Z!1u`3dl9zh?v^qvsu41crS2s@j4PkUJC}5JMV~O`~3NTu`+p`a3^Mojr#41Jp@A8}tdno@YNI0qpd zZ?r*UAAJ*xo24>@*g*PP<`ey2ufdrA;(w@W`#{*2fRZlx>N!G5p$Nk;jCe2>g;;U(FCJ>%h}%A&e_`H_E8rT zM<)wA+Y3DWd_2NjN6np`?VZGUd2Rms1dpAg8Lv_aRvGrI)`ei8tl98!Xma zxF$MH#Xa}-HJdWfJadqC8(az;1D9g)JX zxBv0Z{IU9StHX&859k9UbPV671;Q0S5tsGXyg-e?yin7m@lutX*K8;Y3k&1g87E|e z)zs2zt7qrGeTBv0<32&@#fdKAnqX!fJ0HcFjn%1ItW{Qoa^+%=Cy5{Kv$@2XAB?8? z(QfIrUtBCo+&g?XW)o}m?8*=2n>B@e4nJL+shi(d*kKqr+axV5)a|S>MF%&=d7tMh ze4XiYmt7_6Vy~~Skawf#@W->Maq3p?mhvT^PsQE|4Of*CrP~D`yXoF~7Z(4;)3OzE zhG6UJWa-rH4qk5VhJ+@uvCK2)&V3;;Du+nSd>S97W~X!csVm8=9Lg##6e6`UlBw6U zFwxaPZn>bGI-gy+vEmL{eceZ~81Qf(bPLCbjnKL+8zV^pWP>2TQUl zR>=GgeIL2375fiqKYSQC^WCYbn!lFiK%(!4yYqM_|F?k8Z8aYuETqIE0%88NAq*g(XzV_ zJRNkAsDxY2w^i2=DQS{g{m7Y97KRIqsnLj&^7dH3K4fvcR$y_VlI-4L;A&!$7Kvw+ zcu>Fs`}2@09c&1{t6_vW7BOdYeb0%)=^AFck1Jz2>{K+ngKUbsg^D_F2X*rN0u<=fMF3kiy$mfZwg;FR~2r|wcD;gA7?^u~0kmH)lE)_B*R z=Y{)~x+C$%x`r=LA;U`8GAuQDdHFc+1(F*01Yxkc_9XjOW#M3#iEk!#oSdBfEsDIs z!9w587`pxRH}S}tDDIWgekM?HD-0{OF!I&dzkD~GtS%Al`4_)CkG7oLEk6Dxv-GQu zk+-1K>UjRrYVRhS=Ty0CkylHyyu*(_XlfXZyrv_JI0~=otEJaUj<=>Z(R$LuS+sW{ zA0l(1`Q_z@OEtn3`xd)hjdmS5eiUTq?)y7(+#Ec?4S1N`FNUL+^ zU}W7`X5N=`q}iqEerA0syPh()(Ud4A{i1G{E|VsU<@>)Mh+GoP2)*Ub24})3$|0xL ze60uW$qk+sLun6eit883mxp3Il%gUcOq#%}j5?le!pGXiNcMU!cGEw=rLm;Nwmx8S zZoa7No}QlGXC}Q$+W*kIt@e#XDC`rz6b{O*oOpFLIDlTL^XLNXh!U`z{`JM)99DIZSMk*G zI_IUI4{Mc$yEQrADgI$dHk>;xy(}*jkKMl;9z1eG9$lM5!j+GW*2y}g6?qE_i-l`h zR2jo5#)_+1F31;-rY0J;`df;H5a&mPx~vZr!-G?g*MN(ROkhBPDtI8acg?g6Zq)&8 zdjn^D`~9;=6>ni{Ls(xKRr)ymdQW3^j1CF8kGiaMj!2 zOB_i38453r&NaRJgDU?fufGR)WAE<{wX6=+hjh4HRy9;51r1@Zi;!Qxy4{WIXc+Q_ zUn1ouGX%JH<8-oYWy4!l#JN2ia=a(3z82YZWiq!q?_&2|o7Ry+`zJDva-s0*2?xb> z!K?qG0Qp}WZ~yyr$fDr*-w%EMKlCV;z61mWwJJxYePx)?&pX20w zRZ=3NZR9;a^7WORbzz$}zj^bEPC#@?nVI#T&AZAsH&z|i=R3?=?`Ey7%(bfa1MI8A zKcr`7fKcwJ!T3A4+-Dn!oAWuo2E4oUEK(x!Ryt#>vX8{t6zPrw>d1w#f2GuABF>ns zCU-(j?zb4aZO3FTteZJht}g_pCU~Tq8bahmU`1-_$2~U)RsAHh#9&3Jl7zeK})e|3#VTsor67)5R5oLx5BYtVi(;mieX) z0e}MlrJd_b+*~u$_nvRdC8uZ9hz`dcsAcoC9taleOG@Jid(E-E!QXH1iz8xys`Bmm z3J)uBhlGd@Mc*)s$0aGp3aM#n9eO1f8uFAw&NPcIwk6iCf;5uj-XAP1v|l|H_5eZnr5;08I={=M4;E}WgqD4MER<#AbWX=(B2QH*GP6)Gi0cUVLC zg@sqpL7t7K%{5g3c@~d%T;3cr+GHx7DAIEtYv-mr%Q$|{NsX#GwL@iWvTU|dcdS;k zQ9wwJDXL$ma;5E((PqZ(D+vVKOrEAbHu~ox4!5qLB!a*9cy8_OCdtJhh_u|CwL6%tpWxJ%s~L?1N_^&XQ-=jy z1`{{tQkPabDid%v&xUkD@7>@oH=<#6zCK58wVHCevcGk)PIfuxj(0-pd+$&b6y7tB=3n|i8}jms9kSs4SaXq3{37k3RofU#8E z^OpMa^rq^$(bj&VhH3*rS|PE{c`Z$`cR114=VdoWtJ2QGL*DQeRxGlx9h)&UEKvSq z_@+ZGEr6sjvVrh-`^(xn*VJW1OujtZ(Xl>L$D7Y1uTg$%T{WhLd%UOIQ+tos$jgTe zD@f@;^t1Poo#n~V-edFn^*$!R>aq_?bWTR;hiM&Ef1%MG;2=K^X%4^b z_Y!L{fBn(KCI=FFiYWUn^8CH&gv}@sArvaVgi$hw7ix^LWmEmCJv}6-@Oc znbsveE;aj)77l9FvriNZDtXd0FD61N=29=;otH8rG*Csy8)O97S#T_wmtI|1@~nhn zcO%|5u!Z#t9bIFKLo)AgEuIlYIjadNr7^9Y5%L~Jtzx4kC=!o94Vh2yocw&LcyI#c z3IGR6hKA>!D;V&n^eZP;AGG9Aq2+@VY?I48lP&5(c`>9tm3eZOf9Of}7+r2z%^i%? ztBK|}&RZ1m7CNn(5I4bF_+5Ewc8|}mdxI|ZgM)*lD9N@z++qxWtt-}T!2z^dg7{$LM$L?$092y-eR+U;l0Xba7_|qD`HORq?tV-u9r(RsV zhZ4n%MQQJq#94&&wCzNGy`vh7P-M*&!hKvO?rYok2MZ^rIkOD%PyeK(q)UV=c4gBw z{s5A>=B2=HrA@kW3R)EWoth+z`EFI+xAxJ}()yVaWq58Jwh?JYu>Hm>KJS$|Y*&K& z&~w2q{4iQp|1V#@Sd#$1tGNMav}#wbYwdr0I^ng>gAaS8%k079GNKgtg5AP6$zP7$ zyWkD%9+NJcisd2tNZ(pk*ZRoRJg$a3e|eJ;u)3n|;)?k!Y@MO+`urt8c17c_Uxl0t zVv`gTS8eNJb^Y!@%7b*f3L&!_OrvF*iLYeSq=u0Te+h2RA7ED@A0(32;Ca2ZMf!s- zSS~mL{A%m+`Sj$N;FRvUVOd6lXEssEi;LhI%vv|*a6-*3llHM${2dHn_ zg*O4G_34rkha_+PqkSBCxRtq44Led+aSwpo-t|dWI%McVzU`U1{wSsL-_0s^P+Qj| z!rI*6*V9`vXTZ!=MCrPEkv8kO*hm;k$|NZ&LwYRPG7+5A~uT}@g^vn_Zr zx48&8WFj}Sgq!`(&!-dX$=J`Si9XnpfiOi&vT>Awi*A<9!#(T?4ZLuyL0ros*-n0c z>komqDpZ6c|)&a!yG&ZV6lYS0@g zYQ-zp-NhG7K+?smf0~*@&pKDONQL$RgZ8xK^v45?x{b2b_X9Jyav+x{6{$+E&(YHF z)VN;s^(u4QZ`q@3T(8BrlwW?YS_4ySXeYV$ded>9KdMi3V6}g@jWlED)z3^vKh@G7 zz?p4szj_d#!n?0U>>-LHBBOlU_A_U2cR64Q^tZc0n7dnkYtvB3<S8(q$LRhEytKdF}rcIICxQ&<|_5s!iRf!if>AqSq z^M#XCCcxGM`F~un;#0yl`R*oS1R+YPyDmBVa^-S^olwapQLz8zL$(-YV1+)=XNME; znUiEkcg2ouuzvnnpP}bww`J4P(*y49krH7xnFVswVX|Vy*xqEOmxkwv@AwJNDoQd{%z&xq#W9PZapIskSxCSj zlwlk%{Tw{a{;12W)0-M{Yn#M6RRW|hMV z>8f0Pf}}XJ;#q$hi?ZCgYVqd0ly~Iu{HDelj8cD63jYa1ujy)QiN^w`3`|T+ghIYU zKz$b6T?B5Y%^KK;CetR1?t>?IEN`wRd&IpXv>Ui~kF;lXooAJ!9Z)}_LDGmOH6Qk~ zh^sy{h!Rf*Km4!L!9Uf6{nDj$EaGY)_SwH&P1{XvRv-N|Sf@C1*yPrZU5DQ^Te&qQ zNHVWE-nwXET`tfgf7#GuG-dp)>r_u}kH?r`i3$_5-Eh2Xi1g-ili(Qdp3RNL%KYKE z!CIFtn~oo1X9bjAI;@m&eE|ni-fW`i7}7G$ZL7N?k$hhG;Fax9*vW410Q{l!{N{U$%tiQ)U^aoA9u0T~9M>_@jUJ24F@H76qHao4BETOK2f%{6 zY)ft0lX$&FW7)h{7aH!C4xXLhrM}DV_3Pr=&pp1cI}}b7K1r((r#EL+r~CO~w>X#Q z31DYr+xsSAXMVc-!CBaX$!h~xpAhhQcgDOR77LEyYx@KaemhyW|89!k$p}#`ea2mq z8g#{E3}<1p!l2^It^oVbC!_9kd^sr5ZU2G3@DkCoZTMV7@yqsTMW>3$htzt)?GcSe z^Q<{^`L7qb{J+TO7?v~WJ|NQiyq}MHI9Dy32e_9<;GKS09`~qd%muSX9gkq@DDI|y zninSOwN#N>^TAf5n?DG+Cnws5Ba3#=bWiyRBZ{g7c3po_mu($eo3Ps?yqLbnzIGH* zZrH92j>goG8?j=2UgSqI38S~2MdiDHrL;={?p-{tH_jDU$6G03bvByt!u&PeTVjJb z@B=!Ztf(3;({8Jr4z|0K^;116sEzxVdekGFUTKQB{H9$dNmfc-IF??Q7@rv!PNbTMU5Lx_kNZ5gwy7Sy!Cv)6vPzd%MM6zn@QU(jxmf z%QtFyBs0szWSF=&F?|RidoW|Pf5S=K)J<;p1z>C|RNg2yMyxlo|8$o)Ru@LR7$Va5 z2)kMVYC?-S@)c_nooV2~^|4 zmCMRPDmTdLJd#Bu)7Z=u#mm{{3*TZU_hKk4TKJQ558Kl1)NkDAcp3b1g{K{;I8$Rh zzErn0`T?E7v7Z4?2oKhJ7Av9(Y1hD{**9mUH}jiu8BJOQCs*0RJJvkudW<~TiXG%Z zoF0dEaigAQ2GKvbs~GtAwzy%U>|_!_5Brs2o{5e}@7GdB=z0d5G-ml_2#uzqu!A;@ z(>pq9oIK zMg77C(WJp5H9pc?KB30o+pUMJeGS6B^^?0Yn74As|yoW^l##m{}_)l{joB5)*wI3nd)QJB;I>tc{ply3~$d_IG}P1 ze0;yE?@H_OjvOhSw7!kz%X_#NXSfarWIu`wQ{?ED*Y^$q2avU+Lw@)tK%-B14ObGK zLiY!rr5>R>x28siV(koD=Z67zfg2cITkB`vd~_&lWr?AXtK5#x$0Z}wTdg*+5mGRp zZ{yN5m`o9}n@pjzhQ4LDj->S!oPAU>n|ykyfYOJnTzf`08=OBZgZI|GFuBqTJ|M^&{jxdjan`Xt3^n!vcwmCmp>#L%~g}elq5i ze76Oj$5j1rIN^k$=Fz8E5;qs|21DBZIF!TC^Qdq3GkqYF;|s#>f*;D3|8bq&=G%1g`S=POuTh=g?VLFs zd~d#1dV@c91HtwSzVNGp2E*&%b3avlS2k+Kw_i-7zU9~=f7;|CM{`~nUpcPJcOZz=`|H2ngmYC;&c)%q|;1vXsN>I;;P~fKG=1b?y9P=dVb!cxj07awBn@XUn>A->L;_a)6>5UrL?b(E}oD~<<)A5uEJ^2 z;5Sz*H)De*!YY>JTYCG55t9&yDw-YqTFvrSc&7JpTqTzAV6q;Hu?^^@8?O&bZ+<45 zr-ejp^s2Nxp*fxCT!HA^+WkaF%oe{wCh`sb&oD@U>-?yQe%9-~(t+Sk`Nt{)K@*)FC7f9B@OLVSo|m+N?E?wg?)D+ZJaEx{dCXbwxn z_mkI&g|D4-JDPkJ@?+MM7ma}>IQ#6`Gh%%gI9^&v8m#QQq}NAP3#IM>dy%~yWRu?@ zznj@V<=n6dkeV=%_cgD)#tX>e~4J@_3Hgn)2 zge_WgL zg)Ax!*U+@FpAV85Zl^`)SQ^9;9%lB)5fYNKgIc>&Ec62i2J$?6Mt8RfwCh_wjjZXs zmX)WVppZ8y(b`>*c5*HtEc=f%lkZ7vxDN*sJwg%E@fn(v&KrB6_O>*e zxT)WI-pOKZ(zUV2(f~?yISE8d$@TKl?{A%4h8-SPEl)lt(0h~9QW?J8ee(_Yff(H77%7@jLG{IJqiYCp z>DE<|#b1oMyf2VQ(GK7(`*o>Z(v>OkWW>v|G6}0=&ubR5CMVOf=RCs+3G&?w*+5W{ zhTno=lib%}%O2@nZggc>CRo(}oD+}tS!{8!;QBNtdM(RxoB1+4Mx*qxLl)y-=cFAL zC%f}|uKCQpVmFc0N;bTvzUN3_7};TTvrbd`8xQ~}r_pUi_fQIdT-e;65vbp4Ta^>rba5S~qwHV(sdr$S^;2F5 zQP-sfHnplsd%v9&_x>EV{VlQDjOoA*Ok|`O8%BT!cg62e#vSUZ#2t`NGLdF|Rwtt7 z#Vb#=8~LthUW&oiBv0=g+i{<6H@5ty)n$zN@9K0P5ANm>d2kX_aFFbVdE*66DASE_ z*k(JW9_$6YGw-xen$YsvWx?QSBBvN&&mm&QZd~d$+$SKub`qF87-` zc6-&}3EcBYtSr|TAcFcHafG~H77ae_2261cP_YcCKC7E=T20Q#Nx&T-bQpS8y8^2n zlgq{6jI;pneO=W;Rls!RH(r20SwdmzmxmHOxT*S4Y}Kzk3)`@jdhOkiQ`7b=d=Lk;Gr z>byAj&SPc}kQi1Di#iVMpBjeJ$YW(8(g_7c#r(k$xKf_$5XjBv-GW_f&m3fBo%;aZT=EaRprFh z7!Xzfy?r{v%6nM6S9eJ4B^Zp%Y%=w+f$Qnxte~)&AIbrfS1jpY~UeeP^g+u*S!F=$7A=|A?^i1aG-qv0fPENr7L>!P~CdWRWvL7ToqiH zyP(rheJ7ytejwF?F5cTw4OQJJoZLLHAGH8O4?)x&QlFTLs$4TsN|11W+wptbGzJsH zxD7506%0y6Dg!pbkvaVIXRLu=VL_~Ll5uSaX|3C}R}zTx{U09g35BYC@_QRQ!YJNt z;Hg|6B!o6=eTeurK&xKaiH`gUH6rM(T4 zGN69?l;O}{Qw`ufwyl=@;NIsJ;&ao6mb ziY-7=aa1hz1!m6t4z*S@aLk%UV!&}5_kDakohrS4b}V-g7B0}D70=yP(Fok{cugbV z3`>9F-ycvjIBhmcMay?%*td^Hdg)>&WV@(TCHM0Bjq#$XvRM^|xhxQe%|~WIwjnr1 z%qr*sGH@E{FiJeB=;>NASuXhMSl2E^#S#b9e4{YiV6zvK{iFC zQz4N34jP`;tfj)3C;WE*$pw`33GKKU#baG?Ucrx&TXw3_Q7<$MEQaY5k|WRe&RxfEFm$uS4pX{aSl!-;Cm8O^x?0z@`SE zo6lwpm@(1hRu80X#h<*BU!I20hAco$Q?uYwT}B8ErrB%f&OdG#2MgW?SK4qc6sgV_ z-LJN=zNBNWOY-ocWe+C`jzBKUj;fDP|C`{9A0<*N6J`Z*Y963ksz#y>C<#Aw1v$~D zaxy`OFr9B(66JA6_YHH)gXEih)b~^492Klfem+oimWslwbnXS0>^F2#W+!pi;l$)f zkHD~+!f5bCN^v5&J$bptwe6YVD4iuaF%9!+X=kv*X3`LXm2GPBVH2|Mykun8Wm6pN zv%XS|s0SQ>%(-xQCRZ|xdpzjq2hxWdVg#*Trk?9Q8sp(53XG&x8wunuS=P&t%OtFq z!3Oe9`P12HwGgQ>F%LnibJkPEpoQxTD)cN5CpPUU-E4HN(6B;#hdZlc*7;Si(3hIL z*H%^cf2X((A<~`jPKfCFd+pUKVA&7@wGWjK!L~LzSy&+~j9qa1^&^w|=4HwEnoW9; z-UB%d{);&?KgCdAU8Dqo24Oc_V1w6C(A~V9yc}N-_g=sa#-(WGyU4&nH5F@@qfp@0wN{fifx5(iF4xn~Tct zbPPJs_GiS_GK<*XTpLyOO)u_+U@uL#6Y9yhC|>1w`&$slF)<0?f=dh8zjFK+G}Z8f#0T0Br&s^x0Vh{RGSIJd|maR<5ough#((y6N6-=mRJQ~P>}9Z;Y(`8dEBIAmU(d(47L zj>ahw+UdfTIyr{Q>THxhVa(6H-MN#OFp@OVRcxoh2J$V5^rFp^M(=Rdv;cX3}w93^%Ich z1_Ivf&`T{S5J0|dv>>z~=Y~xwcFZ|}U+Rl#NE4p3j;1EAFJyi}L&`?!Sz|(+vJR0{ zF%*q2+ix@67$91mqNZD+`=M4~`t5Qp(Wfn1y}fdSzajoeH@-}4Gg@pRWHjERLmE#C z&!4$dtJyByrDG8Cd37Lvc+ny)dmp`iYHQ&IgUk72hE#R2Ygog@XffBu4O^31P2J}a zpZVPry%yG_-d-(<>{wp~q4J@VC$k7KND1lKoKBM=4vwfFRw)n>a;r0fI9`u$7OB;e zi{Hb!rN=V!nb!6*Y%M4@Bk2-It69E*cj<@pFW_{7+i}qJtwk)Tb?4a=U#or1Jp7UE zg%DLI)sq7aMIkNQ1-hI7XkJeQ04K_Kle$9`k5ITahPGHl(6LTWXGpabZE7&95FoH8c_OqZ;hfqmB ztU?#D8nmQv_+4$Yq^d%j9A%o-AjW1#;$Wenn@o0~692=Y`67);9pK#JD5KLxzbIn-g?*1G^H&BiJ#FyJdNa?aQv6+-~WRM%$)y}O5EYo@y--=HeBT^ zNWz7By=Iqyhx$sCh23b*{~MC973@USP3t%BcQ}Acmjmo)3}Vh;1P<^@&-Wv}s0JhK zsu$H~wj}oN!U|ZvgD}(>s^AV#F{uG{uZGn!9dFr>6DYYkMri`r^B8$a5fP(@g~BS5Ysx|OZR-JWKS0uBKT{h}`IDTAZM$!SK&!6isD3lVV+H^c z1}ty_Wz9?Qp-5Sz-Id7~0(%=@hEzBZV2d?N6_jGmK<)mBjsZ?f-}$R7|D8`ykXrpV zJnz8o9e&%WnBt@%%8j(9s%mO#67?eQeg-E|6DR7F1m5KRgy%e2tCf}fPcEVkd!;=B zD;F6RW!7rJZef)B&84t?RKnQnSp!R=vQFflAkk{pv58c#Z_#JsB}HSiU*~%_@GHBY z{MHzM$SFYaRi0P=gHVe;r`gc7^>qG}Oy70m81V}PWp~^LKW?10ReV0%S1JFr+2H4p zv`pWk90E)D;%Bp_saAC(4zhm6U;d$E`c!3Pha|o+{#9nC^#;F|wy8;a-9NYp9W5_bzitbeq(y;){3K`G8oYtH(CEnFW>w&mitIt zRW6vRt*o($?=7zkcT~fs`aTS{)KpJS_o|M*gKV6A)$fAb+<-7%2%P$f^d6amwUC2# z5*p)ISY^BcXbimbJESl7^aR*Qb7o59U3sXxjrre`N9onDKkf+Zei@tX@BqO&8iZn{ zuNpGdT7bf2K2j(2Kw*mlr=SN3{)j!kWr5!)IzC&1Me@G$2_ED$kGhOuEt<#h39gte zTiN0=RK-SyJ>IRAY*{yS(u7hN3qaJbrLI%vAXGu{XBt&=P)Eg%4qv1;g$&hafoYVZn8YvPY?Z6sQ%v7`f!qZKhD~s6+a{$$11|E$Ym{0eq&w35^27eb z6r?dj-BC-BZLyDKm)au9PB2B7SUR3}81zQa81t%q1zfhN*MyZ(AX0`Fff#q`v#QVj za*z1{#jL;Z>%*SfT6W*KEGpuuqgw%xNx*$(fGcx#I@A~S zX05;lTxgQG0ID%Y5Y<`&#c{ylt1KfWDK1>_grZviT{@oEg&S{feYg$8f&-9Wcg9lY z6QQ<;V%k$`Z=`O#my-`QmTCw_dyQ*?(u#pXNvl5_{hQ_AxHft*#2rpb!KVG5B6j)g zfEA#)HL^}+y5Odq^okn8Hz0d(YG)4Sxg#AeFcDm1*g24+r85fP*HIr2eMKWEb(l~^ zhVdxgRtZ92&k|7V8PvxFWd3Y)BhLP~x2FcAuEUwJ%@NRSlGb>g$@dL#8($%n#Ll+f z@B;$+F)(XxK#%0PYv7e0Lpz9dP!++L+J#I!+ofuGatX?tM9=sFYba5FBAhaS)Ro9> ziQR&+6@9^#9&3OjAw!(V|YKS<7i{KY$>7q6`7C zi!_S})x!NOpiWcHsIs)ktSU$Z{cjBzLe~$gl)Zd5v4rzkk=TQ_kdHw>oXiy0-)}3d zW<&Ak_d`1R(-DnIOw4?RBOp1LwQSgg>h)m|PdX?Izi7+)4Tz~#Y=&NxA+l6m3@EEusuS3NT34Pypy#f!vMu;NNGh@Mqc%!P!3h2T7>DEBxaR_ z;)@LI*QpDwI(T-A4m`<>{fYi6tj?a`5}jWDvLk{L15amv3kF%s_3Pq3a8%=F8oWL?%A}y9hN1 zJ@~;9DzP7QyN2cS%w*F!z>T4>Mi*>>@uT!INqWrv%D&X2w^t=WLR1LQ1rqOMBF^bv z;#N{MoASg4auJg8E$#)dkR-(@o@tN)@XQosLCr)rqk^E$Sy>QD=%>ObBIENIOMQ1f zSR;C&m=DQ(kQ!SB*omc)SE09|DAuEXx59&FB=vq(UmxRp2asuH?k`!>Zmh-#Zp!{xPA_j#*##I-j2h9b?VrK9Z zZ{x>8bAL3+v7W#7BdRk1;tl(=?KJY0K$#Bc4*G!m$a%g9wC=~N%r*zX&OL?Leim4? z_91J51xC`zK15|?d6%ut)J;#=`HH)Mq5v^a0PD`9>Vb8sp6%Nd?WGbv#4Vy$(H^}U z+q0yOB84R@lIBRj#gV$V5|noV5jAJNBbsjVibiu3;Y2qmEBl}iEC5U=t!Kq5`>iPh zlN6NSQ2V2G{d*$)=;m6}CXqQ~?+uqMv7<<|*N>Ro7_L1`0 zdddWL-=w9A(xlx9`>*k&p)^hnTO8ZszYM)#g&$Y_%ghgC5y1`LhNUGL>_Ds@C42HS z;e)*btp(T-V<L^AfQZ@i?0Y{ zZpqNeiE-GUs`Oml&i$Lz#A*3;F4aV-@*XjZ7j0lL)?yPJ6P(&z+3jNSWpITyEzWz*=`9KPw)z(9}q8w5D*_in!% zT&X-)69JgI!7kRmCevx%^v_7xB7%D-J19D=$KQj0FKSXCJ5A2 zs6@=gCjpvk=(*ui0Y>E{TkWfC2y<6q^W_~oOD4Bx=@r;?34w_J091?UZF|9f#@%6i z^Ftc~D0mkvq(8%cybT(I`S!Ei+`<1FY=~@iCEf~0aKT%dyL4Oq5bGHLQ>i7sby{%c z6vu!0lBtCIM=JsoI&)Unf!{FpP>~s|%?*`CfMg=$Z$6-AlEw-z%wbF0x9|_Cd=9&-!dY_nxTaHG4Sf1G7`wSn&SX>hvL z2UI2YffCNvU|3~+_X{ap*=FtZKe?5gt}M zYGowNLc3}V;S5|F_oZw~&V)^bzEIO9C=+gRg9SUc-p+h{;B-x^YSYwlo7o0oJ8~a- z)LVaxDFl9n2l>BfAWzF3hEUf71mUY7sFi}Ml$C()5peX_u&}L}09;>CAG;iz`yGI< zl2#{?2(z}tp}!CK$`CN#;~<&c3$@a$aL6y;+@~NI0W0_pZf!(PIskMuQWPC;{d<8P zNV%!{U)bfe6B4#?S??YyeTmk3oJb`=0^Qp&D@u za;!K_4MyZGWT7n%eE9A97(q_d*w+av>V9ZSvfSFG*Z#&$s3RF;Kh&9nA$d9nQxnEM z!tZ%eQ+kM-kC%aN01p~49{k0G!Mzq|inl^n%N593?{zJb+cHdpKAzRU6ZH~` zR9EHk(0mTGSx&JEXb7MEzmp8h;198ANay7!pSd6ALTWM?(w2>?D)U&4*3~>19x&`W z){UEt8@X|Ys5hLSBs8r%y_OT>sp)-$9=Ba8S6RN^vK2p6$ULLw(DnM-}YIR-5wQNGSUFl`hf)A zcpm5tmkHF-JR@LVN27=v9(}F$DyHKI^H%9(#Z_jzj1|#(>`a|GAc+w0zb8-=`VHu& zvf3iXq0(3lG3WrQbB{^5f<9zEt)H8bMtY$@l~IP(b-cx^M$G6d?jUsYA@WTUN;;&@ zd#KqoYZ>va%C3?Sh8pp9|A;$Yq(Ju&e0oH2un(KGmu zfR)9apa*PlVTMH#IDV{Tj$LnsvVlG_EvPHPC*b%`Ij#b-->)!5p2tY1vob%%h=vuw z#0EKp=^+OW1x?Ybv_L@I@6KQAr^h?c^tunT4)25MCuh-D0ha_qBheWs%%HZY$B^(} z5OW^ci-ucRLpnJPicpzeNLEykz?dHjq^HgznGGBQ{!oA7Q9w$HVUT!2U3%;N z7hea?A5}8m8*=2rDY&O7#Pz`e2^9ZQk3XZ~VI(X%a>4YY@8)W+H31X@4vTK+UU@sL-Ub1=#Cw$ac+zgRnyfa z{a65==1YQEL(Czd9MXy|Apn)C#zoI>*kC*Nn-X;&@lG!Dh6nYU7u1Rt?=vYoov6snit0RRQ~Wm5xSd`i5*z zdM>*eEB9_=@^?{aGK79q^tZ&8`kG{e%kFlm_Uko;@2#qSUd@f0AVcVZ(~~ zL0bQ4z&1!o|A}y@8y-zRQgxG)ww^GQD$gYkaG9xR1fpJAB*(5rMIFfN26euv;_9l1 zCKmbUxftK_z6&e0PROU0;SuN(Q=n`93zpYazYmq1Xr;bh%UZfCpp2;I`20l5WDkW| z(E15!W-rCf!!x23ulPn`AjX0OWO)Bv>O$j#7RsQJL*VaLQSi<07Z4)B(5a2sGjgyr zn)6Wu?AyVVloVYZ1kK@i!g%tlLOB{>0pcN3gU7b;&?&9q$@1ZS(B1dw_XDR;M{^%Y zfkPoZ5XPzfY2RPr7wl0+gXT|+Da?U-qN(aVm?lbT`n!5JTqtxO5@nh#Nj_9zl=8}guuK1>D;H}uT)haKC3|~705$g3nKYDrJVr$ z(;-w-2x1j4$_<2>S?|=ZS3;^`{#=1+l^WEAY$#s6+~jnu&Ko< z;8>807F=s7cEW6jDs3 zpo)M>7b>m$MW(@B3bg-#FsVQ5L{^`J!WTs&2Lpx-F8HD$I@A5N!Y-gmz}&cmq7~zl{-}SLvzznrW(|{JDz+!eY&=Gl};l_Alc*kzM4=(e~ z{XZ`g)rQTOhAYzP!2Ga z9=qqaRJs=-;x0!Hx~IL${B;7#bS?pCm67i1&^k;x!(_@(mMiN6?Vg=R&{rn{tne!s zrI7pc0dox~4flig$=bXGf=F}T>t48^g58`FN8=>i9oK-w2{aB7J!M)Sy$_iZWM;c_ zVHg$#0-?rFA$E9nL&_AJ!JX0l%G(T5rcS79=F821sZoC+5IBrSnb}5Eaj#~HHBcXPq$X3SI|Nk(e}C> zul7j%l4;j!0EEYiCQ5jLpqJlk=@*jez5>z3G{_QD&Z2$$?)KI~uyB?Iem$v$#WH~a z)^6cig73z1OuIiK?|AWIpnX=G$Rnc9R#k>HEVtYxYes5H2GKmf(8r0C<%5-<^ZYjG-_$hqA3O%&ZHk49~L-Gf?`E>W&^qhhN63tIpeq4z)b@ zj`QLU%EPEabPQa3<;st>0dX4t?T?S#03`N9r2aBA50bGR^h^hlB@2NG1qkU~vGRyFDuQe?{y)3Gol$xPvR#26>Sh2+?LB`)3O_xB}N0>9@5~#F1@3f#+a` zJ{*duVQU}(MNbFMhEilh(<*coFQMWx2$HuVMxn(xqZjJ&{SQ>4X-_5mNr}?T#@BT17nY;|<|||M3v<$-^*-z^gu1*zV<6eZ07^ z-s!^Qzd=NE|GQ&fYq0YWz5%hk*%4ii!X=+0~*|Pr_iB%yIA2_A- z2<2K*;OF28nW6D)$ac6`ykNRTKdl1!zK;OTK~`+NKc()2VP zjV~x$t5G8ps?}xVO4g3l+ zmc!CMYqvWT$C7c#1QM9?eO&)ni4jwI3{)qY`vl;jB4nKag*!Kp<}i|!m9b`!FIo}v z;6%+5W3FggTDd)+C_tUt@woQSH3W7jhn+76{3l~5u#y#lM7i1*zk@&Pe{y;EW2CtF z=S>X$jvKakcozJm%q&r#>}rwuA^D}lUUaEDw%sCpaQ^scp93lfq3UZ1jAF6m<~>NO z27$0c-$~gT+R(OKB=-6`Pw+(%zz1kW(LlwU(=m7KAE4fM(6zAuO=%-BTYq61>Q*MR zo(toMI0-;qai|!NKf{C;ti%V&VQL^9zYVJWY3RHaj#bHtz;ZG$GI}Be7bz1S+*1Ke z=|>d#@E-2)9`@$?B(y<>Kz5xuG%V%4@)07zVHm7Yz28O}w$zScG>WrJ2ex9l38nDD zAOC+!hIqQLi0kj^+c#rjm;Xu?BAMnrOfUSG;>-V(IDZxq(ICCWoD0_QXX(zHBWrfqGz)*&3;zmJk207xATzl3Ca=Jiq+iEa^vj&CRx#XBbM)LFVscDL_rY6zf z8DB)!k$boOj>%GkdQGOxs7jXrLtiB*t5G|VaUFND_5X^Q4?BS#cP5dw>DWV-u)=dl zlpjkFfj7DDQWQuGS%5D{vmYNHh~47q|9pEQEj8gwLI;RBD5;}N;L)Qf66kuupbb>F z;|FDyNB%{-asTUy!qT4^}-;pg8&wiW}rlGs;9gkwLYL(K#GMq zP=q{%;@f!L25Kk*0`L`V{vHpQ4-pKLg8Ze(MJL3d^T{$&5(b;mS1f?4EeZZe60_Kp znf-6eYL0Wqe>OsB?&CECq19XwB5Xn7jp-@}UnePUT=XX|h|JLBcAc5yy*I4}HZ@oQ& zW4oD!xTK(^#A9^{>Yj%XqJeobz2?9EYV`q3o8Oa%yha6-cCA@oEO#Pe00z6&WmD%= zZg`-Pv2@fLPc~82m+p7S^t$mpFc(!O@MkQfhLI$I4)M5cB``~}Eyfq71l2$@KSfyC zF8Lz~H7-5|u?e3LxL~e)9x+?h{+ZGTTCd_^*Is##`7RonDH_Y=%PpxWNt>hEJ~=R#x>#zy>1Mpp`bfE z#%#n@xIcY2O*r!XR%+2zr9dIdP8y>w02p~JsaN@OMssQk!pS2_P+vos8Mux_Zd9H& zZ}{y#q*m9{N*hm(=3Rg`&SZyOV+ymUR1;VLmm3a9*@7q&3O4|6{sbqJk; zbTPyw)O}wac?eGP;v|yPys1KEZhim;ohZPSPJ-6q0BSgZUiGY=Jecj5Y8qc%c*(jI z-*8)R8-6fNkqD(hQ)xz8e=dEc?&m^tD{wMQ)??TYeuw&?LGN+m+Q8|r72c~NuuhDn zKD7JmzIs=@wm|#2ssQ7GBS1)9QW#be7X-N*oQaI2Qy@h;4aR$fk7hCx}$` z!)-0n@}7qC$Y@aRR3ADxIO)EjM5+XM31g_ND;5}7o{Yxs6H4~*fsPp_eJL2wS^`bv z`8*0JR-g1JCpL`0nHWJdAA~r1%81b{R)|&@tT;lFbq$cfqsgmHgmH?N01B@y`|Y5^ zZL9|R;82^>e56+Bq3CvtPjF|4k)jsKRK;AzORnS~p^eGQJG6Yz2V$p-f4TClPJYLGkdj8I*{gsB`MZ8wK zdPC6gK{_^+Tcn)hMGK&a2JGH^-T3o|^$NUCd%bEuRRt`EE?p&v!8}$KxoI@$s!(!v>W;oT)X25bj)x8D%GKrqKZN1d zUAMb|2;6u85xSq+LAZ0nBtk3iM^-l39iCqeU*9(6;h*Px%gA!b?T0Bdcr}cyx69r`-3yF%w;Ve^xWn(mI`631*?L`a)I|c!yZRPE121@S2w7<8V>$)U7~#Uc|DLh@MAHjcBH38H`8fx9)sH zakA+41qj+l5bi&LB(g|sZP_pd`4OdQ9X|yZyacU}v9G~pp`%#<$raATf~t2x0zg?b za6Nx-3(%U}UIi1zit)@XAR(ZXP0*JF(!W#R2a?Twp$z=5987rG3c~1ouN98VA)cZd zn9ry}HWn$brTuoof1xmtBpmpQNB4~X$MP1ZX9CO#QF?wprDvwV`NM^THaB4ky{#$Vo}V@Ntoo zS#?}r%WJOQ%%8?FOW)UwEh{aR&Vno{dZ;nT_J6c@-CS-u?XR#40!f}m(CD1r%4 zNg_rNL_o=)fCva>0E-|7R8W$Dh@gZPRDy&;lq{hos)!(2l9DW-ER{@4n%^z3w|BnX z?U`?8_nV#F_lIAp^xhZt`NYQADbx2**PsdZN zw3q|TZ!ziALp(gYl_TcKGitW4e>r19r?~=DqZFsZ1A(%WRGa9;b2nmgnx%uRohFO* zvFu{Kj$a6`E2%*(g}XB9xUKMMj9=n4C4TOG7~{9#skTIu(=(K~*~9^uTXK8W6osWcy}Uv)5xK}w{t#nytHbBUt=Mn1k!m2BGe|6WSO5gN z$&M9=;w>=`?M?(?PcYJCLq6lb2-BCF9#pha6|RuvSiYWI2_l8yuI}*1!U&jwQ{*-m zL#x|H-ccVC4~niEBwM5?iM8xgFxWAl$U=aF`#bCPUlV30Kk=`Lo|4uu4)X?vAwnpR zdnU%m9st*Si;77djBeUcO7kHekdM4bA^AQ#^jg-C1`@x;K4LSB)cYRk96{3OGf2jY z0u4?s0iQ|DNEJh4io*Z22GNgDQO$@9r437!JE?PmGh>RKrxz@VHp7vth8-}c1UQn` zJFm_5U}xh9fBJpS{hjj(ZTm?Vk^nK1wys`_L_x8HZCxK;sM0oX^;D~fEwnSZ1^UxT zB0&+a!9X&or~|`m|1$bfH~%Nm@5bK{ z{dW8n(eJbNKQ8)J)w;{ncAsUb)|P+Dto?h>`Fb|&Ijo@(u!!re-N~^&|MOojb%=*;S+5TQ z^)77RpJoL9rm0Cr3WY7E6W}Pe;tpoki;W#kN_EM~w$4~XxH@WBCH9M)APTO1GFS84 zb0{f>+7om31vV4ZF?q7Q8eF~23aLfMx~2<%86(pNenVGg(d1k{8WkG=&}Y|T#rJf# zT44K8lJ^`XzfBSlWkyM$%BJYWZZ{CVdl))qPz(p%vGNuZn@dvt0Qx0GOCkeoDSLpG zHj`A9h{N#3g~?X02=X|7_-?QjncsOVPo@9M(3h>da2K*fgl>ga#chlc8lDOZpg?(g29Rw-n#YgN8qa;X$4Tt1iZGm}nu$v}++3XE6 z`(MQpq?b3sMAHlqn+FU`H#zsS8nyr@hVpW9`EyxK6r9AaSj`A?j~tfv^>9#`eC<-i z9{l*r@5PwO^&4jfMgNNlf_+ff`IW5*ew5@(7*22?eYH@f9;R>qp5oMSywZsA#>2-= zDVwGsc4FZ@p0Wv@hkL7oHeO=YuTEX#p+~K+IyaW7u=b?D;oZA6G(~rnWek+XONyWCg8!|<1*C% z*VrBCMi?-^)!x!hn{^3})>R%5AS|U#F?wOQpeVGO7R<`f%H@br2Won-n8UJ}mc?T< zsT{;-{J{_=V~%)|^C_HnEG;{7@mKS~ENmaEUdy7CWCp^njXzQTkqc>Arc7%=b*>HW z3yE7UJ$JGKtVWI(w=gJ$P(C31XmSis{Oy9h?}0Fs5CFyjS`?#{Qws^MJ^5m9A@xCQjHn8iTj#d4aVKSk;oRQ9HzAATO5AMEp=e6W8Y;oPUJwV1q- zmjUZ7Z;Yzi(NVCokCzd4O=%YFF67a3NL>W7f}@iHaY^TeA_u&IW#u%F{=kT&;bdL8 zse}OSEp8GDo34EkNpEocK9>F+MU4NpK5_pnr0owADGnl!5A#qtT%x@WCKYjy@#r1X z=oe|_UFZ<7o__(4he1{Pf;4vzPlMK+c^WHEERrm;ER4CiZ&zDy#s{t#R~P5|d(7u= z{LzC;^h)`NMO5l`>Uu{5iENg9*)7Rvc>_kHoowY$mc#MoJAeA_{PF*>HKS&&v-U~fScFHfGOg|mRtyODA%vH5BNuFDD9CMR}(bAVaTgfLJ| zo)eM1lYi=rK+ZfV>l#-mI-O2U7qOCH3VlkSU)5jqK zVV2wCD&_X3M8@8_wq$zC{oP&_PhfxUl(vO_B>l9>^*}(iE~yJAKaD23Q%r}hS|TKW zuDvM-AVFk@CyyAH%zHoKoxSu45U-SQKZw{kj*7xj#Qpm#+HEr)pKQv^DlRsAz|C6d z>&x)yW-=>liv~Ry?hLNnYRQJ^Cl{$P@e|KX?!^ZC#l=_}HPft>cGDwfTgRonHfWBe zU2^j@ln`82c55iOhJGSF$}{#cJ=TEP`_Z;bt}A$L+RTC53Gc7)3^0`rj|E@r)wZ9N z@#-4WPVMupp`UZ3?Q7>xG@X_Y>&{NG?yGf_HR!ar-s{ETAC>ZpW>H!pjp$gQNc-zF> zdv(^S3_~r0p)+bm?Ft@WxW;pLc<;4Xp7Y>o+}gHb`nr4l)ipIy9iW`tbr``cxfw{b zKRXyBDDisf%DlK%q<(y4WSXJ?$Jr>pvWe(&1&k}{8LU|W2lU?Ts6&}6*)YrTOf%~S zb7X*78_|J8Kb$o=_77w16Tc~OMrlZpgLB(AkQ?kRGcbNFWjBI_rFiDH`IWC=ka3fh zjz4!8P?J^41rSoLoWQ0F3O|dtCcE(g^b1dawP^>R$jK~BfyyE1$gP5XS7c)L z+dApsa`sp1IfTJ&D5@2woxs&r>^ql8mVw+Z%LBN&jL0ocA~2K#wa{eKRHY}U>|Q;Q zsetY>`}KEcnRb!yua>O%qgU+%puW2b@4V&;;P*N>HK)NQJ{L*p^ygitTOL7u2fKiL z(uR|?KJ0gH(^tZWhbhR}`qQc2#Q!gq8*9lvbm|&Oyk~V=7nhP~Hi$`8R zDDEk6)z7^+mU&E*TIk91h&2lGj;7KR5RbafAtvoNlB8@>^`4=)#-k_P&VTuIeJ9W; zDs1sEOrTgc7N&@n!&4>Cf7SjxVrcO5`CjEj0ZP~vJe~Tz-g~!gE)Xq$)mrFPX^T|V zSde1LK^3Kh-5lhkBuA9{PZut4M{!QsXdH@wuzouf{6an+IEP14nQ2}U=teZ)1hS5Z zidt=hJ8S~VNDDX^FGf^~%p5=~dJI=X!qkh5P-)A$h7Ac96zJ;p7aq_N#*IvGw!c--+uwD)q;!88WJ3)XCK3pSHiHa>c@_d?O!oaW)dqqeox6%F~UqVtim&CfvuFUM>OXgA0_Yehf{M+lDdvA+-?cHY{FmghV9hb~}A$OmUK z57Yp#Vm|o^-D!sYD1uIxST#2;E<{JUk5Q)1@R=I`-$PLcuJEhz#@RC58^-{{MVgQL zv76c!`3MRSh(2Jcbr%H22L_zt8D0sUgTLKZ&VI)w#Fy?O&+S&5;U;T#3$a+8{c(Ao7Q+^N+X zNc&7f5I6O!-=Xu&e&)HsPj^S~bM?cXW>fddol?M{$iVHDWx;T-_TfDNwJ;ee9X-9f zaR~{f;(AYx-J&z!ztYgr2}&hk>JJGsTr?NpvEJfS_nskOhNrqbi9uWqLKWl}UiTxr zvC7oC%_|0%>ZP4Dx+7_&*^KzHY#EWQYct!VTGd0=UZckwbI*vgHQ(+63FCn$?dK%n5&Nuf7T^w%d zx@^0&M)HkFbthc0)bcCB-~(i3yoMrkAfAEDrc;|qng%700zcL-eYeH7{l)!ML4k_& zFQC8#E9|NWv`b}VQo;>AdMHnJde3NIxnt&{V=w(zS!H1J+krGhVfBE%Cua^q*{QFM zWEP!U{_Nr@-y$J5@V+n0PA`8>gf92FH7xGHMIZ!a*|FC`@++pHe3&Tww)I{|Nziow`{D#%eN=3;rrrnN&>&H4 zjtM_w^J|MSlcZ_kVq|z+=S#fIH#hhe9)&Hsl}4I}ifvHXMeV|^I}xn)Se|$pC7(xd z&qHyS7bKB9#6!ksO298q%uDjH_Ew)5LvRWSB#hbE=q)-8^RK1?bN?uB(UyY%WB6@@ zaso%%kJH~sM98*KdsY|m}fjK(2Wlv3bXzT z5wr`=4L-UP#9-6umM4)E6WdcQ_2+3EY61o7XjFB?D?H+Cfj`foXBI2DU!JHxN$!ND^Rks6X$CEKo zZ2l+Fh+)NH!27dEn-0R$Y@FpXhxa<`)BD^@{Q8>*?{HA+7U8T440M49Gla|6tUHn; z$1PHuV6Y{hFkQ}`L2~HwrgW*RqB8DcA4oyASN5D;>S^XJgY-+5i9m literal 38645 zcmeFZ2T)a8*Cl$CD=I-$ayDE=lpr|=gulE-dmi4bN1PL?X~6{W6Uwvb43MdVgech3Bzd;IT1A-oU}u(#FBk%8|S}Xgd)&=8sj~NgB zTX*4rD?kTl@+~XA-!C!g0>}M+zkG4=fBvgb^NM3|HTPANmCrMHtbDQU5ZUr88TS&B zi@tf0+j8J$R{30JvQcB0!H18<1fSfu&QvUwTRiH`RGank#U~~h_dfLGU-^tzdo|Yb zyt-5#ZdLf*F+6-fUaO%Vr`PD#(~|z&l!Na`KUans4`#2@EYDI(kzzi}@436KsH3A( zzF$0SP1s0hFE?9V;j~E85JIhDw$uE*g}s`{;$xAzo3PvZPX`4!RSyfZ6r9Ms^P4nd z{8L}MhQHsMCQys+e1Cn@v_0;o>)Q95?wboMWA4lTVh5|?$r?F&W0df@OnnIFI`#Hh6q-Ns;iXx5f0mLo+) z|MtO)EcG-nFetBHsUeiOb?cT4I}N@2Lf-7Sv5m2c%a6$V#zgM}SN;hTu z@02k3^7MpRg{bH5n^els$*KIAv;BA&j3Tb}!XGetC-l8>j+)-MjVSYBDqC&GUbWh~ z=cFpRtbI3HPmoZ~^lIo6GbOkfZchX<-v0HYO=7AcR6;FyRuLZPNY{l21tx7>_MV%A zrp%smxUF%Us`!N1;ZDbOp~P70a%Sw^ z&@W%$`@|}LwF@z`nrEf)8#RQi>jW~mJ$lsnz@Xp0>BYqiR)JN!LHlR(XqtGLoIMWD z`bBhCf%oCTM5MO$>E-G}ub@e}S}C41D4V#2y&G13~%Bi5sJ$MX}em|CH{&}{s$AS(&&xQ%FHIaD)n@MWa9pc6hBM8r^V^1=A+-y4^VkSFPrmCh$3KL|G zss0)UBTj7Ilde>{JsDhEYByCA?>zR_glu!J_Xf9F*PkB~ZyMa*?oK=);~lZBz3H*N zLhhEmSTZiE8l^dYGl+{($Z0{-sskRgcrWSi$NW*dvI-97A63bSB5d1h9^_2J6&jMP4OE{eK;oPr;7(Q4}VvFmo-sq(Une2;Q-2{n|Jm9KRCdY!oc z=V&;+5byHt2|@}#zHtw((SQb%7J5W*lL=&|;d zNp@subJ&L6vvSZRHbT@IVoUkq!QS$R_adL%He|LW0?)M3W(6-kGgJ5_u>H5MihEFS zaP4ebVpY$Vsu2OzX;(OBuI16?nKw1Pyj;6GpTh`GKhd_4-r0}6dOzP`w9@rgjnHx> zX_(07fNdL3|9fL?t0J`lgFt$jl0n;A;%=NTi6|8;it?I8f&G=*Zk#lu=bNq=g#=*% zocr$j?8H6qU6t+L94~TMF>6c1<&N{V_2e4w8@I;2z4?D8`PNh{S811f^<~|kc+Qk? z>Li`5xS*awEU(q=G==!CsbT91))Q1{g5N)J<#9QhhR@Oxy#yD^W)h|&d0?_&$wH4+ z=Z_Zf+ppv`G7x^qsdAgm_deVkPrjEZYF2@s+8KJm_qY_DT#m7vqCPDw7X_@`@&R6Y z&-HH;?Lx~el%Io-eTvpwf1{#sH+)L2#tXOW|Hb#D#?T>&_qN0~ZrMArIGlixu$#60 zOe<^f3`KaXJqm@eHgmZB&4t6G-5g@AicfEiI*)UHN`;GL5#mCR-Mopxec_xaZoS)f zv8X?lEZ&aSfapd211%J3N~}j~E(X%Qdiv0&a#gDA?+1lbVM3|*UC{(o`Me0@LI~67 zZpk#cuUDQ(tY!XPf`$5|F?i;T3@(2^u^n~nXSF-DZx>*y+!}MA*q#a}na{4U*5Y+u z8gXE=3z&RyQKA`39@-8oc|A_sGEef+NU5ES5lzR$uuZk@(l5Bq2eWT)q2LV%wITk+ z->)7%!~CCD|7Q#^|69$2qIXG27e@75Cj769SSN~lcG+4ISLJQ^d*~w}j)Qd)S*0$GL*lYj(;@fTX0svq(;5rju3!h<(;WnErN;&T-cF9vLi5K!zPc3`I zNd}h(F01vFvQa$dJ=eHx#)LgKdNn2hCZ5WvT$4FUoshPRZCg1Sdt`(voR<@pj4I4! z7LpogtopNcHK_%kmod`*ajF2f6}LxMSFLvJHpZu%`AkFWTM>7s?H{o=H7G4xYvYVjV|uZ#?|YI7vD&K!T0de*wz}adr-q&zS-M6Ebl>>byWk zNf}5Ywttg|mY1cCElVDy-%zm`dgl?PsgBbS%DuahI}*jjHa;C}r$XQ{GWsFx$Qq8v14(Xe$eID;gSjZ~ zdEVDc0MgIr^Cj#oEIQxI2cC-{Z0f_2dMwnD+t7CP5LbOjc~w59P*t*<(d}n)E__QM z$}Ja+2UXt&-%w>t6tb>pVXJsQ7@ilO7a*NAzPdMl==HLry7EMe(OiR$d!$2)>15sW z4Bb3Wgjf*XfH8LBUn-rdffO877Oz|3B=0Em_d5 z77+(RqHKEtnq6hbF^YV32R#7Z5_Sodh|Vg!<-D?73_w_(`L2=o1+V@PM?ww26g}1M za~WwAJ9Hc$&FC)BR1Y6~XA(OdO3SC<`IMM<;KKTRKl?@-h2;1>_-Av`Q|yApd#V%Z zX;lZi^JH{d3>-ht`(7ZVV7a!~*>ItCZ@D_rnuBV+j`ZFeC)-~Fh-32f>%11E;YfNN zTVKk(37Pt)No({vKYrQduY2)=JSW7bSn-t%FR*B|7)sVB=?%tQB(G2E%(`E&mhJxI zt*P>7mOjE~hSI`LlSTx|F2PUIs9pqR=trq9Gy%>KiMLnHv&#FlqvJXqF|kn4!8UsJ zp!96_PTk-4<%DRvY3pCM1{1{9|9U1ySGRS2Nq|&vH4n6j$N$F7_beU@_Ktu zpdl)qA2V@3_^MB9=zBWOR!^B;Pqhr1p%n|}NH6=7!^gGr)=8|mQ8t^Yht1)y#KZU6 zaZh4DY(?Gk5Gx*e+;S6O^*Tdi$B*w{Uw5xgwvKL#9Z>pRD0rfx%$>G4EEdUPZoIspqYThEnlKpyOJ7f zeZ53~Utt)c2_lPXyV(rYvR>=oq%*`;3KDTy$`$Ll=I$-+JMe@^nx37<%!LB^h$15+ zdsEl42kTFj6E4nJv|;>Uv!w9w`iCs#$Fta|$Ph*`QC4;~Ha6E^ZG5s$gT_&XUZFgF z*WPj%NtJoBm;U{<%X10`xKx)D!fxSGBcnIV4#Za3lF<37#hsbNoSp!{>*Wtu{{C_W z5EHX5FYww|rU|a84}qr0>{%5Lnz#;EcPj{CH}Y6p@~xE&Y1LuQ6^_Gg=vt3HJ9k+I zp}X>_aIvcIwIpJ4IV#rGuN4HDKUx4u`3gi=DW7F>{*`+u1=Chc36P8Tw|w!h%*5F% zd<8byT@JsAi1R+!!8Y!|g4Z)U1OypTWttbSq3ftN#2&P~48Z<118~s|Y~vAW5Tg{^ zUy=Blc(9&UXrtr&GdYlLO<)rDZp3NW$}E;mw_H`rdHK^;r0CW;X*#8~jeh;KX<&x+!T zWc`hvQyd62zHUm|LmgZFRcCl=O7g3SvP2Ggl=g zrLPhsBC6%%`#Zm68$xL{9s`|te;^~d>oq;QhyESAe_8yLVnf<%Wt311R z(WnO#jnxl3_UlGDA5zH$i}BW3oq;D(Ib7#y-}wB=_e_Gb4IT54&13t3jaj>jRhM~2 zkzEKWnMrWZ?%LhPYo7a%lhCLC`0JJ6g+Cj#iIivDJ%QXr@;ze_KZQ~BW`zrC_OuxR zw9vv@4Ln!2x}!Z_h;O+{B7o@baG^ziw!>$wLQn&+(^0xkgUTfm<2k~f)&A?ab)1jo z{ap{6p7fG2w;R*%mxoJiG>t-eD75#?C2=dYB+OW?ELI8&!Fp~o$d?BEAmwxmKA1i~ zuNLP#=9(-SOu=mR9cbI@$a*&yTB|~(O#O4+{&n0u$+fva z#7S$sCx&wvvobVq*2!$iq{vYbS}as?+lMt4IH#TyD*!GZ( z?}l1!+irF1QNgcdS!q;WvSmu?t3BJmQ1*uDh%}$OEXDHpP&4g2#NXBmw$!U_y>~J+ zt2t)gzg8fi7jlvdUk3W7sAecL2+55_B#im(8cv_eA~UP(M~tj+A5a|q>|&$Aezq-F z{SgbYt?&(s8$m^o8?`OKjJR5Y544d;@wO+rK}ncBhxu?`p0# zw!2hs4p{_#+8nyk`j(5SS0m_{zoswC0ToG>u;J&q$zXN`9*mK0p{B)TDBsu}M!!3)yePMN|JvyQ_TL^_f*W<+DGMytmbWhx8n5uNfv2$kJ?M;80`vR$8?^+Vd+wdVy- z!UgBj9fG}JVsTF`!FrQ@Qp-QbO_6c9-hEF3sc%TMlAkSJE&g-osnfThKfW0HTqzcC zvTeZ^nIbCD!Q5!?3|b7nRWf&^Ps|$<36H{>n|_)uP}YCFm~Y&&)!6k@=Iz13Q1beI zc0NugC5o+t`lQEzkkaalFSfg|S9q#D6SMsb)G*zL7<;p!;=-J7Abdp53p8hcX#RCF zX{p;8FH+K4Tx;J;JJ04^z1L9wCTke~x%mIVoeAd*2#Cen(Mh!+G6=~B4_Z; z^@xr`h!T@8m@t%S7$K^RKA$&fS;9ZLUhQa3=^kvx`|JM5v;Hg3GaL`y%ul1+jLmi_kjh+UL_))T4cA}~`5F7= zZ;r)Ba=nZN5>|Qj6b7Gtj!hT$Z*^~d_yDZQJPczXa$Y(dH$mckr>NF2rp#s7B-W~H zAHr$AblZo$wfl`Kq#ck_Ci^Bt)if0f198VuN%MJX~A0*_6 zUBADSPQCx6db^&z=5WvZ(B|dv54{ikH6`UHoPFeFgZ{EIvoYHGcD#nxPGgfMShh@S z{k_daS^b&lOU`y?Z!?D{6H*q+^Jfx;wx&hAF>_3wd`+qR{9KgL^WACpX4mrZYBk=o zQ|u|^skXkpC;GOTwi1FshP8q93l};TtyO^y2*=G`=sA)!5ge;=yOfG~mSJ_Isnoh= z)g#R&01!EU==Bd&f6xgzS<>94=>Ae4qs+18LASH}K_7R0e>L1&Zr*42gXPC^q;ub> zqpyFu)uQ}&)%E&$6|$2;Eap9nfTQ)MJ7|5i?fRUEHtXH!QLv+^@LVbiqTf$TaLa5S zR^h#-MbC95N|Mel&OHivpCjbH63pX-f$TxHw$X~V+4R*W7H*8tZ)svW0wQD|R zH97xl+51pPwPa+6)mqnMyxGgiex|DQS9s1Wh;aHdVka@G#7Dz2z}mjl65-o)V(DG9 z*`HBeHzR%130B=7(s3XB?Mo8br*ha|eV|sZ2LO+q@hDaKWSzZ`#e~$sn}&7IY=_L! zuy4HY39tS|!$Dz+Uw=4alsuPj_6KCvT8HZ0xetLb#sWAhFOUHJVr{Af_LrQi3E|eg zO5u+dt06wq98k89#J6p}E>yrz>GRxBuq`_1D!!57?nM2GKPOo#Bv0_D|Fys(4d6&` zY=dKQCWM+Rb6vow(+-xNyd!^~p8MiWho7DGeHFowkJSa|_}aO3x`=XhKEA|)M*5+v zv3FIFJ$Te<_+z0|vp=O?v~%qCp>%fGS11AKS?lKLed@viK!@@mntZ2L_{{eeNuMFwKZTAzRvfi`<-NP*SZpk$hzX# zl%Crx_D1~-#wRm*8Gn!q$}HS2d!TjSJq#<@Z}u_`2@^Zm?s`R7wisD#b553%CHswd zwvCF@%G;{MDt*y^GSx+KO%lXtnW?BuE$b$mjZN|DhoGi0uirfd;3>ml2V$9Q_bhIj z-TSWCb_1i;R7f(v@6Fvd8?V>LLq9hgeQ$cn{J9ztNuj0E6rIq|RO`X?ZQCiTmFj;X zL6_#j3sRwsJSR!EUQ=O(xIuE?CrXv!U*f2UAnlmCKyd!BPq#T@-_gb0w7MH1dxP3k zI?-!)*6gMPo2HtYVOu@bi;x+{9QVbcBJE6EHV{>AS}uI!sh=a7)wz~5U!xmSK{D^x)o())SYeEa<((pF29VwD){q<8Z+N zD`5$(0cN4lGAW1Kc0Rv670#aLWh+_cGyAb*EXMqE9pkfi+lSV2SocHR7JL2=n|-+- zQm@A$gJC-eKR4eUC(ggT87~U*dCyjmXWF)iU=Wt59m1o`aH8vvw+1PpyfWiK@s3baXTEP$)GKp{$XJ@^Nqhwe;_u(5r>DP9N=h18YvUcxdI*}YYq)*4JRcVK zc(}nrak$t@mWIz-XNk&r)KQ}h#|8we&?7D0Ny|{%zR~4iXQqWUQP9!+Za9O8Oh4bF z&ZPJ9vAoztvDG?KSyU5YXxs~$v~P6GW0kF7+tL9I5Q&aOeM-8I7Y-F!pBfUnsH^|D7N$Uo(v6-xoWxPOuRGOTTxH9G0(%zlIn;(qGHRz{B^EkxV1L`_K9chgsVO9 ziKrB3%j^>y0Y_uM$Jw{v4M(fdcvp_4ZJr9)k1o)ZE29<8?6}xRqveieyg1OB+T0gF z&Uj-9U@Jnjd(}=j(0%z+$+`d%&SdsT==gI-#tmoMh|+e$4Q@|6j;SC_s&q; z81E6SR_N`wYZQxhX6shGs@|Vw4;Il|NUUFOFs?RgFe)o54KG&)FCnH43(UWZLg-y=Ev%f%IdQ)!P7m4>=OKRb0hIx8B}YxRZEcK8}`^iAEF; zgq_Qw=diM)^QW2DP?fuKpM3I8pQZ&{`<>t-{>iK?sZbiMLxNq8RosS-?&Jnrdx-YP z$9I|)m)kE?f~2Xu*gtTvtyS;T*ngOw5=H>1#(B22)p&JA>`B>3|46i6-Cv0W_g zA~|>M)_>V|h(wuutEZk@J&cTG)lNOvZs(-4FIVS1V?Scoh#h=ev5+q-6H9GcQ-A*z zht*FIDodNF{RF&syAzdZy0!(Lb)4(IR^Pt`;8~7~(Ec*^Myh$Hj9uj9%0#Wo^(D>5 zE5*s_>DSs`gcNz$n0I><%VGl*y?)mK@iyVJY_AK){Xy*wVt7o@M zD7S)(E=@>}6FrN$ zgXMtU9xZ2}7K4gsQ~ZiXPelSvH<=A=^03j^=cKd{tLJ`FCMX@u>9X>-1Eb1peYt`X z$d6T?dyj|f*GhnfQl6FF-N^=7Cjy&80H|R zS9+WzX^uOy#z;xF52&Go_uo%Wrp9f^rbve~WK7YRw$!=#k>mLp6Y539Tb;r<%EvvS z^7S>g&^#`#`yNiv0uoJ<`CSezk_BM5vtB?>WIx@Lqoh(fCzbcivFgw^1)!-A_9>pv zkOR~j)9$Zud^EA<^^}{YG3{iu{d{b8oWpY#l)~B2<4Ns69DM4#xA2fjW8*VK_jtjW zyaT2Edk!ENL=-<~mJEadWj%P`G(KYK>_cNVA(B)P|R)i98g z34ayUF1rMua(o;@B}-60U(XRXls{RO?gm2Y2M;s=Bp_Du0=D_5glu;q!{I}uv87|FE?ADyeh zZEEMNadz2&T0SQ+-bPd-_$U6!nYLJs;WjwyT-m_#GaN}|ycaE0v>A7;P0sdREPLyF zSvo9h5tTbPG&>SREGkalV8!vGOI#lX_eJF38)lUn=Cru;#Y5&FI+G;=I}6Rab795$ zCm!z4Oh5+Knw#`yby&4*)5hK1uJO_y}^=Bd8D$ zK)|mB&4!YKMN`pi*eFckDyWO^hhZ+{HT`x|ekf z0h;ayE*24_peKU@W&m821j4+w3y99wt@c2$4L~)4ED&5!`6k)8B@*R!0<-|5AsJ#9 zH%37j3#9h@%o!YC3&r6;RHU~A_LJ;5-)GPo%`x#(n&C7Q-HFk2<_E+;=^lcJ!6Kv3 zGkB;rKgT|~y8xc4m88@qI{@X@k15fEll#$1H3t!U)wUrdERh<}%p^dfs->~WdKnq- zG-NgbDjP}3$Ya@qV4W1Hi7=t1Q*gESu#S20NJJlo=TR*e$IpQ{1`#spWDYB7YDVMx zF9JxNfN+|%_~TaSTo+cBpiZq2a21=~_;S@Anf$-)XE$sUEsKy1@ zBp%>9p<*`ZzYn?f)lDI>**^v zXuK36Z>p&VDb1BnPEI=LbL3L^dLem_Mt&Dq3i~Eip3x5Q`alHzk0@YE2Vkjn-!_;F zG>AoXP20UU7xto?puzK3T6n|&{ExYF3;1myGWb|C3RQ#2%6%?ZbZf*unBC3f?E3FBrj4+;6AyrCQZ7`tf|WJME|x<+;`3nW)RKfh$Tm zc+Pdg85j#-cy^Zl1CAD1x+YgdOprc}ZGlJ{e^5M0^krbYd1tkeQROi6olA&I7UVAe zwyUk*obfQ}@dtrqDKI0ReDNq)0^;)WxGP6zacSO3kZKjoObyMyAxU|poRF>bLK_{6 z(B&1Fh4Np%!0ncE^GCBiAg1yA)gdM(asY`aW~(erboXb^txsU%Xkldp{6o`A1Fj;) zYLhb!UmmRrnYP-l0*`-x%$P$$az6nmQ%;`~sR8yC?VU0Z5+k%3TIA|PAFzxn79U2j z^HbG7K3Aqpy7Ln~A(mselVpU#5P$_zd)^un%Z6Jo)AvkD4UEkCElA^(Zm zz_ibLY*;fkRt3qjp(h-pk3_*tLI8BgPKHZMecu@}Z7uMmDmqgFjJPiO$vF8KZf0Zb zvy8Jhf*cDkS{RxIA}lnK-@-;%GU}vF1y&v9W!gPwrFu7CJA4DWrZQm6Y;b*~TupE2 zPw2m_qQ!`}DZ3DAS|gz+ri^m_c*B_=pu)!q2K0ksZn$LuYF`YFa_jkJ`4*?EpzXi) z>U=&cG1b%sm7%0UQOPX(()4dxd8{B85rBl!A#{F&uX;yb#=RV%`umxXBKFCqH7KF& zLE!8mwd;GZpFnvsK0;oD|H3kfA&MyT#s zbY2IA#o2}j<49wy+^fG|VfVM`!qg{3%7W#HI-{(lC3nFi#nVW1`$1WKl1|p;bHQzm zZr(6Dr$O&M4GHO&;a0HNfF%et8BLz`?GJ_3iOxvDoyl$iFuhlB(nzLLb48TNnzE$h ztChY|mI{Qf+OGS4c`AEgQoGAefKlwI1RdM}oPHT>PT;JIS#@ZjHuXb=3L?;EJiYz1 zo;%`=yG(_yK-5V4JO&XZ5?`U=esPq(*UtGZ8=$`~P;sdd-CKBlkZP#Nu{mY{1)yxmsGPqJCk0UeA9o!emqiGIV{qy5t!(i4f%SwpMeyFC4wGDD)Hk^yO z@;tt7JEiS3Q{0Qq;P^g3;8%1!H+q>bM6)RpQ|P^$eAWjQPF|c4gIMZ`P_E-f@-RjtEEAm>F-Z*1$M2>(IgD^cle zYHXKf1|$K#5rFGOqjKp=8aj58a}`C_qk_h!gMd|4KC_uPJwH|hfu7arkUzHo3Um_% z9ip-Y7^AbICe8vsqK%YSM8?+2CIV=c4{xOM(eqkr3MyDi3%9>JS}b(TQcuV!OwTlw zlOdv21M`p`NXCn&0Bjt!zKe1cwSo{EV_sMHE&UKG)m?X%y&fo0T z-kCe`-}sGg;-8pW_`*-fvE<&PB#oth%JH+LRFy}X&hD?dXi?Bn%^sS=EcmnsS@aIk zUw?&*?mpV}+D1s54veMD@pnMjUF`wZWqy4|M_8- zJv|;LCZDQbzW6A(Y z$Q8E7Ilk3t5z7S{jE4zvueZLMc8tHa@Uw7rJUCR<(TU^aQ$5Lb>dAF-(_;G}=)vr`XRbyB19Au+~s%iiR3ceXPW$3mG#cHzbnILl@O3#smec$V;AAOk%|L-Bm| zS0$$JQt4rI0{O+U$6Huat%EjnC({?F?@mz9H&}wyIZ-+lCd+j`>N^B&Ik9yjI{usc z2V2$bx(wZ;b>+_(J=c{8E=jFM1M0AlVcme}6De!d4Xj}Wh%}qM`!S5V)JD;=9XP%G z0lI#4aIhW>Aydjqr05Q75SMKA$P=EMw~*ef%VNvpHK4;*K}KMIT*0qxyEIZ}_QE7i zO|1`wFy-imCy99ub(3|8iH1Sl{O)#Z-@js8jv!6ip#lEI+}6qZYffix&~O<3Mevyi z{3O`0=aZ)v5q?3=lIh~m6xFGwrF=uCRq&bJ>(1y9n2CElKiTvxXo^}YoPi!$E07x| zwUAHtF+y+v_X3bj(wCHdr4rb9DIH33*fvpYtUaPY%o^x7_0APY@Hn`voAk} zQ536>=_~v4h-1;SKBl`&P{~e;?BN%E2m`i&`~tz;!hyMpqHBo?Gs z4P&G9X!N{xrforDNgG;44hxzQfrm{mZdwf$l5O9heK8JUO@%QB4A`@x_$TziO2FO8 z4J8VGo$~cc2=QuV@bQndcaNFfZe@oZrHz_HK_Nuy3=OWb5Tj(JgK)R0sM0hLk@ z0J#iT#AJT{lfTlJk>3Xd(CUu)gh=6B*;1fZfun^x_PtT7>Ifo#nu5?IK$G1Cz9yaZ58W z9kGy~?pe{%p_5IQ!{zW=6F^k#!Zd;RG1i2){)-96v)UkDA)^%3tw+yoeAPo>2@mf42dyfT9)P!)6iRyxu>Z=0$elV|~Z6J$lNLFvVW z0>1La6U+qE5o;kIM=Q7NgE8cOwh^*Sp!AoKy9uSpXp^%)knbefb8mAZyJCre^r~EX zO(bwRIK+EDY#pl!W-GsT#I#TW4s4*SkLis7Um)*%vh(toL9dzyYYAK5r4gHI93mmL zpwr)%{D}HW;I8MCa`5~vONE?5mK1(q$SSrru8@)4XGQz6nwUz2MH!FYi0Luj;1#Db z-QQRq7Xz+$6&Z_eIAkHQXfy|wn`{3}WH?Z5|06>)d?s`FFle@y3-YTcz!tt5tCCTQ zp<=6w+l@gd7E^w-#m?xCgP`sp2EGogpdPTyUu=gkr6fRmK@PR+ALoHs%Dfb`8LO1+ z6I=ix+-$U-StV6fi*YNw@pN4=AFHKu8MZ&jJErDOK@;-ORqXOurFxOO%v7dPc3g%A9@a$ zrNtld`{6d2#BlfsrX;<8|NhS&EU(`T*Kjs1)^@9mvnGxP! zVl$4)6lehkfI?i&hjJVouN3sz#Z2?Nv@d1K__NA(0PxhZlE$ zuR#p;CGdnC9slPQIL-g}8x3U$ko`LL@<O>IRZZ52|zrAh(!Y3 zT;`-N(69D#jcUaF`TNs7jvIpF+mJr}+W5wWxh?v#M%G}pa>PN8NHS^#g7WH_P#RWT z%S{lT%^xxU^RW=OctZyWRXJpIAqs-C?~j5Ll7>7W0Tp$Dj)#tPVmNy#KQdx&0h!G> zlKbHI;-MM+%u)Ypqpgy!zG(`Bupgvo*`HqF3Bk_Mv@$S zO0D@X@M&#Gh0#%)w8v3{+>x^Gg2XsTCN_@-awjTuc z_C_iqKWa;x)3p0xAZlk~XgU1v6cUV52=aAT?m5r(Fpom25t4h$iS61g^%hp(21D9S z$U!p0WE2zu@7}#z=Vvf-+FQJC)&2O#K&N@%z`D!rZ&wZUYJWr&GslQtCu@Hj#}M9P zAL5{(GIlz2rsK>n8j66^lCeL;(w^tA38$r6)xJ?5y^+|xe?PhQjWcC}%CB|}JqZp9AIsB>1elgzcO+D&8JA1RB{FCgnHQ~;^BLSOr_d;0A}7aMJStzBE(`0Q zEArS}XrAfG$J(!n?kw!MKMrkLDXIh+ncMu$^Q8+gcK1Kt+HU3Q@B@|ZrsG`C3UUNs z$wyHV16*3((}-}7vfwvx$#Oa78ogKsZRrf(sDp|iQq^@oqBbdXs3j-;DOJLGH7pAt zv{fzS2YNktO1aaK+H6~Spv#^WbXtg(S_lIVx5MI)V_A4a?0qVgq&{jq&T|s@nw7T* zD{o_ZZaC&I=eX`&9fTfOY^j;jloqssFN|Y3mrTdE3HAc8|8>iz(dg3_MWEWpERJZ^C|w;HW0o5xJ_Q{`BCt3Nx=N9g?_>s zh!u(ECwMLTScn*e`5hn`IwY2gw&Q1M%Gz$POiVz-00H!_okne51t*ZVa0UECfgri6 zPECSoFUP;&caHTjRLM7%s0W_Wt2S8#2zrJP)#0UKnO5E%S zwirO`!ulHg-R~?UN-w#iL>bw~lf{MKGt5$mk+o6Wap=Y5=6`tY8GijdSLJu}_NZQ1 zfVNOQ$7+A_Sp1Dg(w*JZjSf@B+AqLOTyS@S0;1|=FA(bqk#lhH!BM;IQTG)Hr^dbl zi-p~h<(Kuo%_Mp!G~WxM<;(6WN2Yx7@2GhVK*-wCI9R2~BD*w0j z+3s4fj$F4=(bS}Z-n6bMB<^;4RXiRpj1%B3jN#!GNl+}j8{MDlik%{pXCx<>ky8k7 zR2!&Xk4vp2rRhkd|G~pTL>k1~=B+@guj`^9KODgPv0OtmZKvRNAoDUT@*nZFAr$ej z$m70dh|I=mIo@l`Y!Yxy^w``NVO^zJ9dBHvq06a{$aa^MoTZ7$c6ZI5D)M5@aZ!LG z>S4)$)kyww%9#c%l&q98cY69Q1K=?6Pj(RB0#m{SuV(>4$#_lQ9o(=+3Yv$lmN)S4 zAB&}cJsv(MOL4ptFQ;}{p4f+*2Ve=TJ0(116kmZJj{q1mQS7CU8vl72Mx&N6@v9E= zeXOJ5jAB)wZa|sg!%?STlOK#_>K+KjJx(gHILsrCdc`)zfN|E!vVVHRU;_e5(I_3H zHLWWM^@x93fmjlNXm)F&SoO#nG+l7gIdtAK_|f{-0Bjc|7Fl^xAB%vtC9yqH9VUje z`ed#^ ^8(C01GK+iw4S_Y$97Z)q3?9I5Cn)T;EvzLjdB-q5tw&9uNqPfr^B-$- zz!1nTz^|Af~VD;P$pi5Q{DxMFz1UQX3@i z{$j8=*xx~2{n+7KeZBLT3GmudBmh%TR8=K&0+sHlSI49+#vk;4E@T0nfWndfoDnLL zSOP1XfM81)O2b2dl(9#H`NpMSHmn7ZLZ2%4R{7)1ZK41ECH zZuQ0R7DTP-r+)G#ST2baN)H(<%*TC1`ME`e3lp5Gq|5VfJPv*!drHB ztIG5@+R65zHrf?Z=Mn;0Nel$(le0Bs(qeIT4b;6}qQ8e)`|7=fI4{z}>chQiZcVm-UBQCO{awcTZmgx!=eY9Uegg80inD@*HDl9M9_`wQ;?+Y zl|nx0US7vLd8Tzw7<3?aDp3!YUE=C@RQw@UOT8cZAIAywhyf_2l0N^Pw%)euE)#aP z1r_MG!5JUr5(hW|8F~0nBl!qm>1ipdx?oi>h1P-@C&JnMH~H&1$#Lb890{1LqS(4Y zC)yxHPI~M#>gg!=-kFX9(>{z-?b8#4HQ-gJjp4J=D;JI=ft%V;&Sf9}o(TG~Ft_yx z|Dt{$NF7~%?=FY&*AU%qy6t0YHz|23=vMU=YA7p|Q*FR*1zqXSTsV-pqI@t3GckW? z2v7?gMboJ18To5aVWS51#9*ttHRSeopr--5rzx2Au9T(yVJqJbQz~m{0r`|)!gj3J zU|~A=28{@A9;ZG7a+9B3kR3D=cUj#9g8j*%8YHP{p!}jx+PZH28}9dHLjg1m)KIU# zq4=X>+}PGj7_xDqCCKbA?PANf`CDpa1$aM-zcS$;GaBlGv{2=6KTe6NJHk5u>s7Tr zC(S|QcQmGQc!D5E+yqnl3Umcw8<81ZhIR<*R9BV`p-6?@XoJaU_W{8AnPe{(K*Jfs0lZa<;pI+@UR`Dq+qp|1 z+AtWP@3AWB0`o*U4%4LRvjTwW4AM_v%L|9uS8QjXj=ZD1cnFW;{a$`qSd00$car6a z@a4+%Ilw<$qhRgKWLQDb@v2t(8i)$IQlB=Gnj$*IL5G+Ol2dW&f#exrpQc3w|O zsXQQcUN2$Z<)dDUX-BpPpaH?(On|BX8IgXA(Wl5Dvp7IUiIjRS7a^Im&Z_7A(Z99} zFt8TLVr-XJ!+IjJI7NKg&dVA|4rGYw3K@gNz)W$LiGUue>S~*Gii{60)&Jz`5GIw8 zk*NXYthV|PxBN!k0eJy`+|EP~5A%ZD?!kRjO#$ZtmDf&-zWP@VBiv`CpL@V6$)Fp6 z4zl}ZvHWM*&ZM?5l~jToBG@A=+JswS-xZ=oxBWgC*@*$VWxd*z@KjPff7z6pRJsqD z>?WWq@C;ySw*j~V$_h_pe1Mry3*mznHSWmFGs2bHB5TtkIPj9Etu?Go4CJ0@nG!Yq zMMV8z6(5(A4xr}be&gP(V-RXRQEFz>!Y_1@|tWDZmv`UGKcWy)gk4{0u7Il^L1d zDO2|08aRrPVtQ_KKbBxqIxI00#9-s-6o;r^SLL!j7NhIrV9Iioi9$9`2=$@{h+zO=Qz*btrh(FMRMSM-!~lrb zvUo4@68=W1R6z{4ex(EV4jz zW6Qu8gPjM!_uAxQ!=*F`Q_e-j)W3%$A0$dKDt)-4& z9~3ua4nIMuHeej&@1sP4JSB+ABMG=Gs~%N@QRkDarF5Jzm8|bEYQnQsYt1*zn%CfQ zB|LREo+mj#=4k)BQ|V8l|6yz;{G;1we}~yM&B}Gi9rs*=rqX?3p5!<<)F7uCTCb^YTEBCMsbU+n-QZniaLfr7NX$?FIp*`t1$^ z5%}8r&u^EJ$LQY*W@M_+k)|k4@h1MFraCmHV~tTGJ11i`V4XOZ$>+1hf7(eI*57{~ zeBOKJWQ zkh_S#;ywQpnUh#Y`)8)c62_d$21kNyAt}exx1Dvo0;?jRao94)%;U1)gc<>Vjf_;qxxsWH2Y2}|2n*ECiIen9)^apAFQY9>{ zWV@*by$5i6oJTjBq(=?U617g$LV=prZEH#Qs2Y)Of5g?&`H&v7yS2=Sb}Y&+oeUbX zJ_D>L{!g?64`!nQ79JzY3bs(x@-Ma;X4|rJ+gy0-3|=j7hH{75^Qe8(OgHchy|Q38 zWKMtZ(S0ASR!LL9@|;ft#A@a_BF4ZJ1O9B_W>Q!yu<}bl1E?Ea__=WMAsY?r*hsF% z0QG`~)JEXRr+?A#<~YQ?n@zbr5%+a=wUl5lDxv$0^X;jfg4)Uee}miKQg=GcCO;5% zWS9-##HwvI&v1LI|GYUp>Oa#{X&?RVo&B_Z43|I=Q@qNDrCCk#f9Yda^e!Ll@$30c z6_s{5=A>WKmFRY4?{KW_%b6zc$S`i{guV|Nk+hlt^Rj!S zo1fIhTR=ci^FDyE+pQ#4jGddEIis+x@F~85R;|C-U1ydA+O;dk5(ko$qDLQ0R7Uay z&-}1j<|C#JoP7GUGyVOaS)$cz^XEM`#y9^-(`m+KQUI8X3bS=k`A8x~s+Y8x3%NCY z+$I1u5ui+4t01&U76xXNf1Zk(qVg-#YbT2?@!fWWh7HFV`ricxH3sU_m>UIxgInpx z|5aL|Mo6y{$}e5hVC^y1gdM+7^_mpGUBT|o5P0%_R(wp7NbvlccB-lJq|--jp4JZq zhZlc8_!>~36U<2t<}|9Wm+Yf{YWaTXhDu!DMoKfn4Ic|hVLlh1M%yAO+IRpn5BbTEqJp+(xV^PBiuw_62UD=#hT2#S zj0dV!1sV(L!HycR!P%EfHfV#3qWIs?9;1qtN@gfWz5*bDi3~_IJd!(Lq)AX`7W7>Z z)<6}N7M0u~q`;Z1tG#e*Y&K9lh$sZBEjlu_KM|ef1)hQYlBm;;Sv;D{Yk&KQYC_Iq z`WXDA&Y0s!+GqX!k)AlWw}#y9>lKi5T27oyK^v5yy~So%VMm}E$Tk)k{=eU;C^Tmb zbj6Uq=23f`fC0Edj+7hp$8@C)&ibE|sA!BeyAvD~6lpg8Kh*bABY!hj@!l34pB3=o z%Wt7lAi+9oFl@*1VWb))`}LznN&SPhD77J}-(R23_Lkr*>SLP#Iodr&gdf^*v12w! z69NKlKLDa=cL;c=R)HbQe`z?t?FB-V3cx$V{~#lr(v@Hqb(=g(XCZY1- zOk`H)fACo9nZ^DNQvS?3jne{Bf@#dleht4&16_#;YyIBI|4Qtx(*VW3yVyYH!xAX= z2U(p4%@q)~TxV{kNX|gmGJR}*PcTh=v^(z~5wDw5_j2kzl8;(NS}vopH!tc2-_YFx z*^!4^cWX_j(PBeO)ZJ;?-h8m@AAPV)>fyGQ9&}&R@HDGBqtL<0*?oB*YQ42e()8qj z*_AUpby#b#$R4Cdu%ZjDJ0xI(GhN{n*c~=GPoOQR;7BTl9T=Y77on7HMp@{{tQ^AC ze&@@l%v@-S0^ao-{9W8#ang-ONQApK`5Y>KF# zcib$3cGym5?OTDud#~=Dd*Mp5FeJo<3wLA0J)d=P0xC{Mhivqx8*ms!MF*-qKlMPAzib-m_JRtM$mM`GP0IoluqP{GgG;t-XSF2(UH!!!6{CK=e?>R1hQ)c7MiNo$-y!N?kD(E(^bN&}d5#7yxH%G=-#u_!l#dsW55iNl&H$12H- zO?-ZWS4*y;b1r~{kpcCS7h0llhqi2kb#nh2GxOaRR8}}5fDkSF97ti7rr+Luf(9+| z);74H{9#k0G;V$>SZulz2-l+sO+dE|A!7j#f(2RKb+4{;DFvF($9EL5#4_)}2S zh)DFh0HCh{&kt=OhtxE*=L4#JDCg}`5siTO)M7$1&-L|B+s(cV6tM`mXZF!!f*y&33uNl%W(c^jOf~ZUdRk+{-{u1Mg^@=8DZp z&VnQxI{~rp6nL&lfJ@)n9$ogL)C2R>gUL%UO1SroP#1WyikX1@bC3y&T0wZV4k=z} z*FwJ|b06&NIsqJ?813VMB(8MEf6gz}i~9o^zESEcmxNNfHM;9ssA5n6fCzI49)_Y5 z(?a#(!O?t;7jn*CGC>bg{+|;(eWlg9Apps}>%j!LZ7I>N2#Sh|XVC;8V>$9>QD|G9 z%YUapQK9jZK|I+V7{t(md)5N(v*4gj@KFKnzaJEp5H^LzFnX1_Zvgd#u}Rc|4v+Z3 z16>0)HCp^LbX3K#c|<3yrIm2+{S@BV(=ZU^-$;PbG>Hd&!>U1n>OaF}AZfSxpc3`0qJ5-(gNV$xN7JY+eU&WbNS_7URQyuQE-cYnm>U#m zvzA<_`v*cZkQjCz^`v&UAsQ9*^Ph#_0t(YnJs%u79~}l!$qvHc3v*(GyU?zj2K~Cu z#Hck={y$&N1bCRwqV6%YEk*^ZUxaZ=8*xJQ z!n?1p{`;(G)34)CIId~-fyPCJ%-x;U-iKs<2n{&WICoE2C}2eh+m-DPt-8UW4F_6pFj3L|=n~g6RHAEx8-8OrHqd(37W- zO;{Cg5}XG_DEgp`)|LJZT)x>3v8WE{IDqEf?=Y}Y$T$Fcjsv?klmK5i@+7EWLBnN^ zZ~ZUreR(*Rd%Lz)tFn?NLZ*~x5T%f0Y&IqH+<*pz$PiMRRK`|BgCdzSM&_}>P-#La zLyDw_BvWMSJ1?}}^}g@=_I|%_@8kQ9y^nqWlj`w2_jBLB`}$qid7bBZ5gtgoSgpcg zAjzVc&k%mEdsi+%(|P>cwe3+@^(P_thQG9807o}27cLgBEd14ajh6JNP|)Gib`=H-Q1L0dK%&(5MyM!?@g3WsF%O44=q)3Rtm zsbmH)STcd5vY(R$6j$N$kZbW#_*hlAlrG%p71=V6tO?P)=X0HWnj~@4XO`G_<_JFD zb`*>f)gOGxl3krgEKi-v_CKyeVbF(2poRwcfF z(D{tdr`79>amd;frCY0V?>|sxvF3R3tnRHHx75tyDN}B0Dk~d!9BZi&8f+`eAXL3(2JyMnU))Q@Zdpk&JOrNWCQZp1h`dbvR}e~ z&|iUz4;u#K?9C{9qL8AgxSkl<#Kw*)uO)mloj7%S;2*bVbJb%V$%nJiv#`=A4TF)yz@SZPZ<~i21qO4;%pThIGvS7|U z04t4mJEX!DeJM8#VW&`WFNN#fIhzo(pi4?Ny>fB63$hm5N7||h4ikR|BxpHS-o&*d z8QU6J6&r1)D2q>@z50?_8UnLCm^x!jxB*}Z56LxH%b8jljBiBz+c-;3V&q&UqIp_J z_qj>A1I_Ah-tA_$emD#xpzQ(XgRz&VTPd6E=%R9oG8BY8^X%K9Dbbq~4B56`Us>k6 z32EP!FGD+vJN?8D7a>pJCE`uRyLU|)0ryW{p0{Y~?4I*?Ln#xHFus#8(0SQ;@l5&2 zR+;z;U3QZ48Z)++1UNP;pxI#bQvU}@ zR@uF}DLwPsD_1;5?@%k}_T>miG>JtslaTh_eugblV_6~+E5kx~f)W>!{W*Xwox@*U z3WrRmuR4f@XBpH$T0DokFwm~ZSpEGuLI1JJZLojku`ZuG=qbl=dx=kBMDUW$GtsyD zN+Y-|&P^_EI%fZpYlq9wiFLrLRQtvA#gXs?R~_+O_PlM)(XJJ3_8TgVouFr(Y=iI68n8tMzVnY~9Gc(934h zM%YT@I@aM*>P?m87w~m5)Z69JCuGZ6c|uHeG|N4fHu zM}ceT)^2BFdY~0`D!@|7yx|`99jPh~-^MbtJDsRjRWehFZaKz(K$9UdEdqPaj4;xJ zniO%L62F(Vl#svIpx}?&vCk?|+=C9qeuYU+w-#{i|Nb%co=$=SS36HhVWUC&s%UPs z1RkaSnD;7vAyLc*#f6<;H_ck}y(s9q{WWQ~wB^MT;r5$cL&ssA+E(Ny+xkhKnOgua z42JwDS9-!`ArWsz+~1&P-YWh&zV#VCaW1caa<+6f#zg$2VCLEQUvH^@^!=P4}C-B4OHP!OtHL>BPvpi;uW}@i?z^I%>q7C@EL3u%9|bwFU`2?+#Pf zOy|_9>L`Du2PV1xus-`rYPrGv;e&nr|Vuz(G6ISJIAz+D+;)HpK@L}*EnLfTfkfCGOCF6Q+_?{^vJ>No*93EfhII>|>F_lNSf#m05G}1Zq!@m2Q(6%}Pbgi!nT`sWz?e#qrTK&uV=ncT&#N z?I*gF8H~)gs}=V1F3Uw`8q*wxgpE3xJKLa(=^z*Qt6G87(7jZx(EUj8{k_Sv)avAD zg2d(z`#2!uTm5SOWGh*(89VxCEDwvG5$h(dejtIBW52Sfs@r|gM}?kVU%z%rJN#2p zDISga*mqi^jeGp+=2t+kdK-FBt|vGqTz2>h2G2IhSj5@pv=!0I^KnAjRu%kmZjpQ8 zXJIl%a&f7WHokpudd2H+M@zkOqN9iS)5~Wih&iP;a{Lp1|3~I`#Jl`*#xm!x4!5#6 z=jr(Hm3jxqm~&Nj6+Peo*|9`t4Gc%CR(BNsUGv$bl>>}APVFTnj-SDL`7&gfh78;b z2C2d((4(~!EGd~}Mq=#)P*T(id*7XE{%+yaWoCS-yM~)Mqv}epY#}mI?Y>j2QU*=^ zZwy>s_AA8Z628LJX5hJMbceQf6Z0#w&miu&rB|b9X1!MJGE<{$ok(#$UHR;mY|W2I zHLr8M^09n(tA2z4aVzF6_Ph1P<{g+ZkO*jena4X6Z9Ck2K_Zl(Y~elXL=DCkXd~N$ zN)Caaw9Ic$Xgd3T){jLxJqkl_UOYge%=ZOw!X5O6qs>cqZ%z>6vgQ!GEoPix8hvd3 zs|Kn+nVeQBzE0j%$nAP#VDbw%fzAd9bi=B+Xo07Lf|RHaEWOuMMY@tK8eGv0XJ_&~ zkt?N~+CLYyseTt4d9UmsUQxB&%qi)<^tp7LfykW*gSB6gUuZ~{HKm~Yo4x}jvJ|ll z!A2qrnuPjq;*v*atU?5>mx`7=2;sGgy&GJFy~uXV`^DVY$K&TFp=h8ttSLP8Ep(%? z=&O+!E6OrFxP~-X07d!9pVu)@`lM`hh*2n+V>ir{`4rweS(_^Q%9Jli0(u22;Ul6i z6E_St&2+XU+tsc&q@F;FCOF}<+C3pyVptpKFg{;?ym01JW23I2J$a&jP^`t&RSY2Q z2+3i+u(zs;s{L^PY0L`9@pO7xkh!S+cs}wvQ1wT4F^6$v&Z>5J7C7)JOdX`>h`&^` zRs2qf#+x)<>S#kQHyzbNa#{l%&L0p}F32s8oM=2!1pZEX(_*y*Tdkgs3jsc!GQ0x5 zld=rfe?&$$Hn)LXGb+!0eZ35~f8|3OMl&`Yg&6%#yv;KT_DX`fUQwE@xJO=CTAzMW$B;N-S2nQ<&~(^~<$s zE8N-st!@$Ym>!w)9MOIL&OYf%|Ggwt{fm+Ey$V*cgFO)TdC`O#l;$#aruWc9JxiSv zn;w+ue+QO1sh+CcCVCe&l&JHy%lm|F1C#j$aJC|xbkd%_)0mCZt>(edH@B#xNy;7= zJd>X7PiS$BL4&mBHvknyW|c3BY=AB1a@$bfNLOPVQM1&k)ELNZ91)wsGz-h-5Xv(z z(9{-znJ<~hvu2-K7S{MftR7BKH7;aeL*L!<2c*Nh&W-xQ)Sa%!OWtECF3T){!i^*l z#81kpHvuIY8(BSmB}4)gEn!h)FjZuU_7L?Tr;N)N6CU-+OmX)KzH^$g zy5oeY``LubF~kX3m=U~gAHXF=$5;a(lOpTbf5h@|zZz>8;_G={g@G{s#PMGo(*FP7 z=lst(11-DBr^!t^3s3G(1DiS1r%$i2am89ocl>cQ6Ie*rlRDf`I;w7J#>NozNDeQ{ z<@Wup?P(eosj zE4jsnttwNF*cxOxxw7#{Oux&s;_01&eO>i=9V5|uns=D&n9Y4D=yX-9{xQ>tV?pSy32>E-7r^vQKRsmy=wdG;+T)Vg>m?2x+?j{TDKF&>Je8q&Dz?`NAfiA`WQnfCZ8-=CAc!pfJ_fS?ROWo7+O zF_9JbPchNHx2Rj`Fqq`GL9P#8+ow-Vo)$dn4v3L)41nY^Q)d7&XYgn&k-H&bsdbgr zASL)6XngS*iAGuPH zwX2Z<(S?Xq=62KJDg%zBR?J0;C>`YS{o&m1K(WAk)dS-sGgmK@me zCgZd$-LZA~%AHuvmfRJP5sp)T66Xp~`t>#!z3|Er-0=B?iB0^DpH)8tCm!xb@Y|n> z616dZP@D---ZN$fr&%H@PE_wrht8MjU+X z^1B*Yn%K&Dwpxf~xy_pRZ~--|*V1yfYme_duv1IR@|xuo)(2@`EXOZ?F_2xmqCO|@ zJS+E^H>@1btqt!=A8&Z~e}JK$-QpLwBxwKGEUdn20>Y?F-+?)g1k_g;oyId^sJCOT zyYj2wvggFX0&j*>36r>m=c?H5N85s*QA#5;c|4`bi3DRJPKnop3evNnrJu&|0zqm! z_4WTGQ4HEJE!0LBS%CL#yMDQ8Kd^F*`}a|SNSVMSFuh?h){@M|@g>!QCxaRFhCXQ> z#`alMT63|_TICwz_no}P7k5eMy2B49N*#Wx!VkeMnV!b#vHk8R`S8s5p4PJ@V~~aL zV$22t>zH~*=?yFBLt`drmT?b2l$br1_h&+58R}S}?3(G*$K33OS0#+E-7%LuMzcxy zsxT_!=yV@RjLn^lL|{0u>jITAX&o+l)$KngU+bz#bBfx5hsc9> z5Q5H<%G-1uLb*(6gxyBek`Jgi$t^k9sOpSfp%>tu zmG#89AbAjCMW;VsU$kCae{{z>(G}mb&u|M6y+5j@G4D8M7 z8*X};W1$pfvu_^zX=PTPn9`1qFIb+bsvtNghAUmJKJ;Gynt$=(hYu3Vu1(Np2V(yP z+H6$*mY{t9>ND1^_V)JKgKZQceCAWg#c^Tb6d@s3uXi;YX>1lK8NZde<*E zA3qbP>sU&?7@XCBl~zZlHlS;j=#IP{|8K=hY*syJ)rs8p~S=i+GZ+2*u0{MEfI`*SpwCGa>^w;EFJEP@#yZ z!S0jS>6IoJqci{Xnbz!Ww=v0f5nRUZfI4)K(+0~tstlxoJmQK*mi`&3aiozwXQ+St z9^xPsr6SRc*Nf(z9)`{T2AWF=8vt#=pH3|zaUk9`KP|v@$gk*5Eo+iey#)%~m{J6p z+zg_~a)DDqrnX~jC@~_zA@(e{SwDV7#)>&vP);grEQ{am$`e%qOPsRz3bgv^(9^Rb zsxb~lud!LI{}q4wQJBB}M0!LH57X#zmaj(+a-85`SK5)Xuu0GOwpLJd;_WF3y4rqv zD)ozu=EZLkN%~N!SIla|t-ZsXvrwx%ykv??O8EWdR*Sq$FEF1=W&S0=rSPe2^-8(|OI_+pU7-_}>*Hx87Keb`cNh%D38vn$ySz($Z zVTZYX4H2+vN`dykpx~(M{hCgccMJ!>u_^FZ&+B;H9_TO^jwk2St!qAZRDv8#VwyqO zWrE8@az+nb^N=~(rRiw$Lv!$`YwJU^!-jizuDf3I^n3=7f_m7>hA(|iHb#Ao2h#U$ zJSb+(Q?`D=&MbA2oynHk*YeGEOOJiGsZEv7Np#DNR=*@?Zj*Zh|1e-_6qcsWHx#wk zM&sJi?FAqOIM;4H+9%666<8ph+}K{fu47~6v1CPsx1T+}f3s3W|STx8FlqIbntMCHcA#ENm1tk71FN)=;GW-1mYYO4In*=gpgk z_!~eT-B^B(Ax{|S+yw;%ef~*MCN6VbQ$a1mqb-Fz)0p0GvPvz z^`GB@%0mQNjfb6sCSdU+^116iV3GjmKcJ)_Y=|j6^k#v=#dE_`U(AC_G zch%TBw*Xs4uFglKVK29()8)gn7_AwU$<>f%sZH#5k7G#atA(bq;dK4!NhF#m7(rGg zMiWuAQsl|6??D7J?`x|nhu4@i%G{oeFo!407!K&~J=-x6nGeWscJ(PI9f2voebJmL znKemaa_-;6F1Z8nFF_p{0ajZ%m<0pS&&@~FxAEkjNSG>@;v<@heJWefgfKqj-Z6+W zy+bP-{lXTh9|fPWQJ|DnZe8>1_&1PK(iCQ{Ni+@-6A=+%v9*J0BF6-8nE!+aI%(eH z!(H~&7Z;q``XX!r00&=;0TCe}gf>fa$P)XKcC8+~bHT#V4EhT*#4A@CC(VI+(Hl5Q zX?IH*OTjS^dX+=)hUXABT5L3Sh6M#0+})>s|68U_ofMGgPp%FGC6wsIODgjhOX_st}&vz<;(Q1XTBJ`c+tSHdb!8j zy=5O`pSE;=Gc?uWjxVzswXV#!(thW};NJ8JEJ`>_teBPeT|iqa z#bW10FLfsNw;<@xUYy)`))$9WEqMyFoB^;V7ocr%ronHcrNAM#YoU2}XBPwvOG)?H4IJk^F8 z7tHbwj@(?r?|zQM{d18m6S|R$Z6}{oE4JNyW#kQ5YZWaQg;b~8CYy%UKR@ge6db&T z2Klv?1&h`trsC{ygfC;?d-5xWzN&{15`DiTy?xpYt~+&|eK0VxS(7{w1k)@wUlE{G zBmxhmICdKvlGc-G#3$ugT}!22GuTd?-ERe#HNVo<@Uxg+O2bn< zCIG?%j3>9YnIgp$hXLpjP?(z0lJ-SnRt(ESGeAe>*w!vf+c!xKC7Wcg=3~toF*!-X z01#(c8As1NPE-vnKS^vEJ8dIf-FehmCNa2rJy{sx7})3S@arDHOp8cj1S{@?S6{h9 zKTz@Od%%emA9i96a+~h+7w|`-pyvqtJu*yudxJcO?D4`9_(+EdF9eC8?o-D^vg)-X zO^}&Qx#%Co24tA24 z!zlu^W|r6f`ks6;!KyzGdG|6ft~oK>BLHrTB>*Uz0C)ueud2ySxFn)q0xJ1P7Qlg9 zd3myAUPqE#!U?)gEf^NiOEjvAwZU=?XP7Ht>on6%`|Sagv&9H#W*Ac1g&MvDZ0NZl zK>5PKOinMs;P+9tw1SJjp9GW4k9x!;RS)h1;j0*^b7(MR44<~xkPu0I0SmTR_V@_3 z*|xQ5%Y@Y~Uyv^Zs&EUHs8iiVi?@)gH$fKe58S#VEdgeu#ni|qLqL*RFqj`0k=-+O zVEMsHq_kEr9jYDAX#XiVWp6`ll1Lr+akc!B7DQ)Wc=ML5Da8OE z%)*fl8-$=aWLk;2NONJaqBA)IC+Ydo_m4Mg1V-cN3Oii(`klGQzI}e9Zp49?_-!4= zr(Ssf&s(^pC1HsY{9VX*6i0@CoOau6fwO0%;&}HBV*;Ye5SOGZ4bvel-gRgMa;Xc? z?TKTNXR~+jUQIxy*(PH7QI-qSdj!862>Z~dyzcymjN6Z!+rKG4dE}w@d!~k^lWvfn zUDBJFghdWQLf`uuQkSVXNIJzXw==#|u-CZ2_o9iIhlkeo`%=0#v6kA(h6k5fU*FYe zA||@TM%ywzR_xgil@J}f^Qmp>l6|5!eAyLOzfEkq+WAB0_80I(MMeE+T>H^s{pT2D z<8g6^3TsR}Tu%%NcUV4=68&!Gu~_I5=aDcA^(y^N?W?`*Q=V)y%5I7Gc;b+Kvha|~ z+x)?JkBj|}71mefY1ptD&5Knv>(OgXzus|6U4BU;oMQVr1P67~EHyp8KRClu>00fh zzOls4U8g?y=(jLC9_{mWMG>xza$V`xQw=`}-iT-97TeT$Y;TfE`J`7=lQjR}3#SXO zqXT078jLp7SHDg%#(nSCdCV#BJ=}V7313H#Q(b%Ri|SoZ_>>D@n|}K-=wa4hXd%R! zreP`PRa6~QQ2WY3J_dzRRGG!h`C;0u$2HV@8ir~;3S=0^uX}xp;YnL*H_4_p)AT({ z`b+kLFGqiQmY6-*z4%M4da&lN_Qp-tW^2WtgTt9};M7Q?>(*AIM!Cj((~)aznI6v? zL$my5%+4*>x+K*snN{N3;JsSuHD#&U@X9$^N1k(zZva3Di^f-j0JW>rV_})q@$?S6 zsZ6bG;qu(8kM$hi5v!2G!^C?3`>$8^G@mPF(M;kpA=O}7hXwGs{>ODlE+t~*pmDO! zx@H#Fs&HkpTn_I#*NEk~I`ofCn<3|ex9Z622wbAEJp>>~gQek;&_f&C?B2kiSTcC! zu7YP7fZj?VR;U~*A>x_cJzDZFL&rl6QsX_VEt}B<3zDZP8x68-=iwSu3O^WgaDXWb ztxgO>iD{%r@7-i?X(*Yp=q)6vl>g2s>wy*R%oPo9v|ZbkSZ;F^rQ z6VMypb}>XDbDmTVVj!|QE`ATc8#9yyT|^2oC7%aGH4EK~=QQ}4-W~l#V(SX#R{^hC zJ-zT3_4vghWi3|+*c2^mc6*3 zFVU_Tow^8=BKGi?S#QK7k+t|{HNvIb>f0HJPQ9|(7!fD25Y7rO(&V7cu=^4>>B1bd zwM&c<=c`DQM5R1TH7rqQ9o2y>uNuUFm4R#`DDzudc zBiL=3X+Io`N=SJ~hF`c@n=ntV83NsNAm?W6zVg@_9Zo*5mAuKZHDA|qb@G@S4UzG3 zM!;QsyXX2v*XtQcY)q^QjStbpt}s?zlKDt&@LT7x{#=l0TsAp5^+_cwgLNc{s0)qj z4I0jSD>ZdBq*8ZsJlfoEVBt-xNSzDedffU(_NxkCF)iJj#?Iu=vviorz)nkEv5aKJ z!tEY)>3!ehF4X1-w?0k`XrSQ%qeHN_jEx^c#^~(uVbbpY^9@X?8$V}=rtrKvnDUOVaf%1T3?8s3AfRwIO?@lyGSwv3+%@!gT zbI1u(^Be+MQXUs1=XI-);Uxxb%tpoM;x?xb^EA$*9~JfZVRyGpZaRSe$h}KAuaK-J zOiED@av^iK^^CC~_+c6!k*P6NOoqA=66-SZN!iem8z?WQoFkO1{P~G%6Ak5fv2K?9 zz;2nE4(zI0`lHo7Z;426nXi{*GM}bwu$}VZT%1#7RSIjGrqSWOHK4Zk3d>BQ~(roj)|h@$P|i$boQmx3-G3|qZd%hNiHM3mOSfy3?*|F9Q!m$9FMzIaT{5a|x5Q~jnGVCT`DQ$(x( z*h}CTOcZP z+oOx^l(|@a@vZv6Io^;Y`J884BDF1-DV*zE1Cw{iYt~pHC1!~$!23l_g?qLM^(fnP zg7>HrLY4||Gd`UBf>e+tcs(L*@1nyF#+4TSw~Z(^+*t=vx7;s%fIw$OhvEZp&nr` zd%lL_Uq;B{0r;!jeVB^%H{83ZqgqhWYAZX@HgZi}n4A-^)lI1(PhnEJ1N+}d z0;sx!s#|&`Ke(E0b&h#zS0TosnA5n78^77O1CZJ^R7ZR;|4Au<|OKv^m+IFCGbmJE~pW>#=vJN4&a_?&W-41)4 z+^Hkb5PGt)XbPvv1+gF9A%#UWAY-YBhz||d%GKS^nwvX`eMyQjD+194 zfnTqxp_*+13&t1OX$i^muC298s{00p#uCZ_VmOUp5YXrMM4>JARuj}B=SU3;hnx!q z`W-8GO*&vg&tj&suh7U>8mb_lY*#p{{(?<;*AIA+va2oU2P4J)(ZaKlf>W?l)jn!* zaH@lC<6PoKLj)=hQ|s)y4sLMMascnuOSS)iAiNmr54~h_CMMAnuUW&7oNp+y-4UPn zWxgWYwk`cawg|*Kn9cq1qujpsn$j!#Z_I}+m&2_aOGvdyMv8-izb_d5dX~XUQO!prDQi*wvtGLT z(%`l{2Hx?gCFvq3|HdFR8F7MFSa)}@Q z@QbY(gus4m+of-wuwuPe1GSGj-jfAH{ryEK&MHRp&`7lh>$I4#&rhxAiDpXOq+Y38 zWLA0gt2?%s_KYQh%#bKc_d*N@&imp|`f5cs7bzLA6r*F&;(#-(?j?g5OmU zD1YOsrbqOTKI(9-FQu68bd#r_CYWBoeeSvWhiu)W9lA$v(0kkIQoMw8&Rp!gmmp8y zEX1_u%?TIfkEv}pIE3xwx%q{U{$Nji`uyoz8us5g7X7{s#qO_V7C$K9(!c82;zC;< zQ@JYkK0=64S*4XzQBffY*8;BxuXFp$`gi^ML;U)_eZRhpfBe7otJM3gl8Z_m89ICz zxUy8^->$)xCb9GJ1#ip|FhSDm6V{#CMuPU;f?rMiw${2ktB%7z_!+vk$@1HE<%Wy7 z+)Y|uU!7yaY)z^T7j_zF6ppVfwqja|FIgv-Nj__X{tQ>;OJKv;r%T$ zbYk@@;P+|1zLu8|B+eP@yYlTh_7Icc(@-9RF&QVyCYhm~`VbR+rPp%$+Ui#WM;12U zrQ7()LV23rqHlMjHobaFoXO###k~#8b5D<)8!Bj5Y0QxeX05#Gb)MDZ`$KZ!U0y1x z5BZZrn~T+o%L&DKHNbpC6axA*@&2);gnu@3vs2#k*zsWDBz-U z#rjWf(l-QLRE%Bh)*oY_qB6B_dvqB7b5fc-64K>NWYL*m zPWoUUqAdLEQ^=+9T%JH3-F?%0{Wr6x;S1FA?VXUXSq9_IXWA*3CIxhR_nxxL)n0{_6JPY}I`@0e zmRiZS7S*gh;u`YDY?}G3e6uP|F}Py-;lbuy9a~+ZQ?3OuXnvKjEmgI-(8x@WKA7zf z7xI@J9KJCz+3DwB=Yn78nD`2X$tn+Xv_uMV5jyu@iatD@Rh;@v)OF%>1>c^@`7D~3k?cV-0c*eDpk0fOJcBOemZ&` zhNZxL=839RobUbK`#ttz@l&7QUjLEb$lI2r7}n*x+Gmkdxzg~kpVVNHof#%j;aq7=@0i_PBM0%y!|;@IZ6~hzsp+D9r;H5 za@;mK4qsC(g#G!wx-`s`vppinQu#@wts>JY{^dJLl`NN3qIEe={|*bHSB_yQuWEe71~hmHkI=sSE2y_UHC269O&|mMRgpq4qTMZ1Dijc1GaV@TrSw z;}-V&dPw1c)NDRZIA16Q#9+V8VaYL5P64R@A0p47-=X{GA>ppmDq^E=3>}uBfCQWr zxce~<*2hGDrFSgGvf+*BSh&TI$@Y3DXB@HPl(Rj@3-7LMqw-W9++x(g$mRPBBcIxe z9S6GLZr^LI*;*aIWD_Xs;llRaW&N^HZuFI9pa|q1C5d_rtbZ*Ydh5^nyj>L|rMfW^ zqB=BBWHT9EM8%vwf_gL)@o}~*?z=zV zp`n^J-3Qm6TDWu9GbUgcr+G--Z3-{cpB2>8=Bw*?pBF9|WWLZQ$0lq4y8evhkK9`G z4-abSG>WS;|2((0=s*?LmetxOj#fs0f>Le}44}h}0h) z9km_lbtoD9lkB;cH)kCE zqPDkM{U`$cK8wvvP}zkj$sFu;Al*S&On6#eSD>Aw3tSwn7(p*)L=<8gjf zH(TtPg85Dg+v&FbYfsZi8S`t6786)so@U&hXWKqvX=y3j z*=PFb&`{ZQc4H0{8%#aiXcl-g{kqL>@2!YG2Wu0c{Caxt$M7JB?!`RQ>OU+UhEYvZi4h+iRv<=s$z9~=Qi8=iSFt%)WZyG3N>em|+dwr5 zw-q(8d6N&dN6S>5>=jtRG8a@O=Wj6M)&}v(Nk2L8^vVwhhMH45Tz(GvvjsJa3{;qR zqh&kMU1)2ZjoVspgN^w7XZm5`4`Aq*gHK-O&~|GT){>?u zEa#!h)&2lEHeW%?N-?1hJ_ugXBl$AV9#xKmy>6XG30AGKrH{vll6XTLeTGk!>KIR% zh5wc(Xl#7uv4IxHQjbNj)IG;4evA5%nHeY=V$4b#pEGoFEVKQ-S%#tBFq|Ca@H{0B zCw@^W{AHtOk>h~0fAkae#DI;tZOWV^&z=x?#x3KgpLjMN5YQpUde5F*?Q=KjCmuO> z^CSFRq&KTDmUOGSmj_ql%jZ{Pkfp)7>ujfNW3zdUo@{6|;bJXC=3Nvtn? zE{ZZ8JYe)cLIlVgmNTx=4sbuBk4zu(*g_}#Be zebFYvm3uOrly}?EXtQ@0RXlH-w4p!&U6|^|;nVLPhES=wV-D!+v|FW9)eW z@Fo57bZ4-#@VE2AJ$CQAY;!7y9_*%l`iM?iRKLisG}}Gwtn}v3+7f683%1qTlk1y{ ztqFeH6tY{KwC_8u4DD&JFaQGkC*8?hITiT|CyFTR)8j>5+6MLvP!?^O0nj^-ypp?_ za5FI4B>l7W`8Mg_ztZnIBf~;@GM}0d^c-4$k-7e(y@%fEy}ye#G!Zbz;a1GH&(d@! zAD|b+B=j$Bl-ANpjEonUe7PW0)xLpR(Ct%951wGo@7t%iF<$yp>J}ygDA0p$uD)Eo z%T_JXYfJ5_7B!Fe&kqIg)$^1S|Iv}AVdyhoH{WfWQwO*zR#$$oSLfR_HWl7h_gSz?UC|Y(w$vUjuJMSs_2lB8jp6l z_aVIDK{Edp^^&y&wk`H#rHK4%yWi2BymFovu(5S}vNA0PBOksr^Xf#qcZwyQ_(Xi~ z2LFr|Q{1`0W@qwf7~k@ij@#u0&9b~)Ortlwfak(Ug$Z_BxXbjBH7QZvu6(A*`Kz8) zUFz)wZ7&SmwQ0${^3|_5ax?RD{m6|b?+l6^+REIfE7&AvODuSAlXLvGGA0(DSg5vQ ze>4nij)~*}{}^AmiS;sL{`kj|C-hF|yt?BCHvsgwlztTqwdB-^ntfp0@6E036<44n zzLMnmvzmI*{Ve{Q(g3j~MuG^Pi*a$RIb)&EAky^(pgn^h(C5e!U*5?EOpuZktFC)v zNzx20bOxSbM1l-kjKzI*d3DGi}y$`m)5scLr;o zFa&;~uc;u9MUh|C#1Gc`>j)YLkP!hE>G!a3xREH6@;Sg+mf88^k$FH*qn#Wh3Ue6W z^(E#9$#OKe)emc*NHKRyQgfW2mkE7y?JeLc#+cP{%(fWyT`c#5^FsL=yQT2d3c>V} zNiv*xoYC#HSJJ?zeVRVK-UwYO4xi2;Nb{DCynb#;Vxv`0i=~fE%735cdb>=~6I-n( z9<(I)a7iy|Ic&3u6w?e3@Vsv}Ho~=6RccuimuTVp{UP97 z;2ZdphLs2E=xumaX)XtYJi#n@yna+EZbp%>@(o)WwS!p&!JIUnHy-O{z zrIDi2+@mPv-ahU#7Gag0XTi(G)#&$=VLdpaU*sWA7P!XODex z-}BjR5dyhc5;N1-&}0xhJZ z3_d;qo^4=n3rk`yV{f+M$vJwWyCKr+R4a+` ztykRWZNCDl!BvVwbF7p^>nRM*#}gps;-)L5ALuvdbc(OSVtX0(6he$-;3W$cOA(Ak zaV^zJ#yfE_mveeIm6`wp&cvh*Q8iT7p6M%4!*9GaGiKL zjx?Cf2&>pnGnpfBhxiEd^{2SH&9^pIa|#>=s(crh^7Hc-&RqXfGqV^t$HM+&7vBs( z;V)Hwe|~3?6w(#aJuH)v*{3F#bkpeVuE?4iBlkWbYAp_CFHjsB8F>v$I^3WeMLbF_ zm>mwr7`sN$zxYwLG3yyYe~>3@-n&hfk;erS#&DQ7r@G?P#i$XtIFLvRnGPq%+X0Kp z-e=fO)-ycAt^Br^Wau&Y#500b%Ii%|^8+V6;+OYUt&-aYK#kRF%a=+(Jb0cRS5vU$ z|G1z@{!)ylYea#AnFPlzV~t&A>s1a=5Bm@1ld)Q z$2=)O@G3j@ntCO^EbAg;;q$j0)&l~r)~zjkT8V~WfD{6JvWeRlEe66O4(N+)CyThN zweek0iLy)5c*sB}Vp6t$SYZqXw?0c}Ds4C&nYli5J$^-CuNljgdn11tMpXg1Xaj;~ z85B}jhSVrS98ci&(0V_GQTF@LFxUE5tHQ>2O&c=>3_a({eu@O9{8u?u>o&2W^|c%8 zorG9o@~tEp_5G0-k``X4eN^IP@#-@3XXwLmHhYldFO7C0Zr5h1j%~P>)+oed zb{y;UgQaY4AA#}1G5c-Kdu9e|4lTZtV@=Z(7YMu*)?kyo_^QE#SNUy$HK%M@0ss2f z$AYZiNh=ALSw~MfR^)Xox1Trhj-K z2mCMumC?}WTic=air;K9vO>$VwpMy_NL@hzapg6uzIx;AS#k9ShU!cKKM9m+!ooC> z%}){(h`e6kT@NhV>K~D`*)I@APCO=T%{VT)*t90SJ@j0D*dpb1LYkd*Stz&hJ4}SA z^AOFP|H>c%%SMS>89dwVFJpiAV2ya9<3N??W7Z#WCmSzk7 zq@QO3bwVjlbeRSmxn)!784D*KU=0fdgFd3VAenrFDZ~L9x+ilf5{rWSia=7;%G?0h zH$GlqM{_hxsKi(O#3d(rs6<8u*!H`HLFe||!?LwkrCfgB1@9GIS2+5|0p7oMS=0ED zL|GO0sy%VR*JFDTyef;i#=bThW->NErM+6gtxODJ3G+IHgpL5pLo;WrU8UfdJUTEBMbO_7;e*#f$U-AXdL{;rdx3#$`~e!a@US;hV0 z)b6(G-NrP(V<>CGCCgt8GM28$OWD!5m+TU{J~imwz>zW{w*VN2Yqg{tw&gvi@L6`{ z-Jc0i1ltnjLn@kp8ylxF>^A>dVA+Z5cil99?QQ~nS=J%c z@T~vBVnCY7j!ClfjbEN-M$gY|&QS**GU}Uy8T) z^MU#th*&s?Ak2PiwQLIsPA=o1yD?eCdfr8a3;(qfL6_Vs|F}EP+QiS}=M^iTl)2AU zHL+qcEI=(QO)=%PxSjdC5vKrBB?=e%KSaK|_QBD2+Mra#kn@x7#KSO!v%KGWc>0vr z{f58r!tS#>{ywu@FYFS&S$@#8yB;Zy%%;gv^y=rx?^~rCgF$u><}0^muNC52{)_ib zfRg(h8^A=_Fs|qfO`NN2iC|eujgqck!T8$zPQ}PqiqW#io3W==VH-O@B^5poY`s!+6 z*?!Ap-DUPn(f!p{>-MH9s}(msQ85_Ik4vj>IL1Jz$Y4*S>cH*v!s!kfZuZ_gxdo)R zU*0-hn*L@L7eka(b}vZgKg1Yzt)}n{{!dAy>gIgVP?ZwGEhVt=xP7ZBRdAon&We2@ zB>UEVa6zH$_!!@}Q+b~y9Q7*I!-O47roU!!wYYG;D$jhaXxo$DZ!zx~z~(pDrL(-Y zv&^sdy>$4_{kscr!mRMBwL^m$?Z%2T8G<*O5`v{8{bfAn>lhbHo;-=FZ9fEPIA!JS z%HYrr&iUO&V3*l`#Uv{APmzG6wp;w}s16JJ4xbu$3S?)zyfO6PE@CiS+#I`N(lBXF zBWv| z#kQbk*>0{vhAaKAjU+-W~3}kj5ncHqngfin;PFf^BkC9W@=RA^+Gq zlCGbVyZyWU>#W&$-}&zAxcghp(fjo$tv*5rI8y1o+A_M!pN=hdj#;ttDamgE`(dh1 zk+uq|JJ}Pueej&`oHvb=K(Bn3lK^=!xjQzHj-z{okC*ebG*kAEZumkkB=MJlpw%KV61G}kgc`+{N11F-=@8PvkXm1QPVxp`X-gS)THozBW%S1D@I}n#Af3zS zeVGQJE;PLL{adll6ufClv!#CS%GNH9t<6i`Gle>|TL;v43kE9QW?zmd3h}z1bosJ; zD@T*O*)3>6!+-+xhKWu9#C6^Xt8^w_)BDshOx_sS##LQ?dssV*1KU(be=B)dwKE^Q z8ILDl%Ym7>I5yEYBuhB9uRRF}d!=+iZWa`6(Y5c7Tw15`d9WmO1W6i>^={w1GqLT- z4ArJgY*&^~Z^M4kyWR2w^wIPaIp<><``iaAN$Y7=cg0mFB|J9gi8^lzmA6&KxdQ_u z{wPQay0W>`nzA(Oj`trT^ulz0);}7XSqrhb7@j+ z%j`nn{))tFre2^~jL#d9*!*5E`C&PeHV~pu8(vGEyECXVfA!H`tqMLWdh6kaFGVmH!LJqy6TMVTW6>-~} z8ya;p2v08B-~E8 zISNj~=2IeFX>u!6a>FVp*?%yVq(MI^h-1=ZsfmPq?!8y zeR~0vvO5vM7XX*}HFh@D2u}f{Xw?F~#TAnQJ*wQtGREzz9>cTU9W z?g2A@7EatnB+e*oED>NM$rek&1uewf?@nuQ7q3(yIssbJt{}!=eUzR#01li2Ccwa< z)Y6#p_?U|B)I611FiEuU0`#CnBk2Hb$5PYi%|9sNsNRVFW>$(Zq@k}5NkL~ZtrbLpbO=uh>W7v{m5 zHjWK%IAn>Oic-O$n&F{dCwcl^_NL;}Dh6Rmg;9s8j+&}F)^uoy_Q^+ju_xt+r%~%n zv4l5BC6lV_5?RgD!r+h54cCtsy~?IeqjxT=6%16)yTkEG=uBTyWPPNf6p18yDosZl z#Kn7Jt*&zRQ=ck|5^Z&-P8MepUz19h)2IiLLn0Y#+r90YO~R!t`T=tLIOn&$@jNzy zYdDOsX*pXOHhWY>8gQPWg3G+EtDAzmFa~H-DWlPpTP%fu?ENZlvE?AefoDr|<}KpW znY|K=i2GX#XsDKSC|I#}SP{&$$-iq#gd@{{8i@p1B&-o+?E;p+fBKsuPOYRw#}fQ>!_ zf_I(PBUCWn*h<0Uci`=f3#sA*sSD-aD!-~fq)ec5ESpR&J^BflYgmS6BF*_U@%sEk zVZ-&lD}{kUk_&%zf!=jbD_GO>LSY?f=ksKUaCq1;JXvB1h6Hm|m;#q+pU^F?L zisdTlgqr;Ng(A1&Rl0G|I{SNg(D#SS3AujBj=ohquCP9*)2W@q+# z?|a@AHpl##qMosQXR6#}=;8y7Qau`a&DZG2nhiqB&*uOJQQSFA%76fhld0a&+e=a@ z1g@Oc`Xey|T5+bMDb>$~9?GXZ2+Iko9^uAEaxs=O+-!8f!AwkywHUA9u(a?sl z-#&D|qzoun^e6LcM+)0V5-EzoOMa~{P70W(#N2-l7H0b=>i7}s;azH=Qt#3Z=HEB{ z5>&wP@7bmwpB^c7m$hL(o_IJ1G*yP-eaT1d5IaVJjbOg;`a?>7roO<(&B1SOD|erh zOkEzrEri64=vc`u+9(Zu4_Tpv3b(Yw5pys3q^f znE=&vcz-_tm3c^aoVgju#DIc2$YBWEFE#Y^+|b8ks+lJm&7H6N(>7NR0_ZrtINln} za=+`03TA6*4bXL~_2&30OSVlf&u9BJ1@0sFHk55y)N89$$PuTy`=i`Yt|ew>A)5~PIdJ<~rn~Zn%lh|dhy3$fJQSKm&L=|k zurN_1#TW&&kkFU4bYB|KK}C$_-87IB+p2sw)&QR|0WHtU{{eFMtAUiRb6JE$@NTu5 z*ra_t+E(L0XT}W_G$F5FC z1%a>&>!7n9LwltEri}$^A&N#6P$&WbNPXlj|8W#~>vx+Y>$1ydnLv5F-o~lW?M_9- zrAl|rG;q4F+#Tltjms{#dha}WJT7NR_+~IG%f@WEqf(r-RFLSJZ7OC0$2@g)84Q!HO z#21@{yhMbIuOk7g%^V&%ja^+k+5~&5ctDwh= z2+#?e0TZxL8Hvm;N+=fBco1ZktFlv-ORx_{uAb4zNrVq2uBzx>!0;aAzuu@RNM;4^ zC_Lj2saeCX2Q4)Ub;xp5^nlA_0Z*mnP#FpiB^AG#J+6##L0U12iKT_(fO`=E=&%ts z`sRjS2(+Z;8<(9E>XRyaha4D>iMRgd^)F7AzAZ1IXPyO?O%Bon16#cS1Op-{lW~sU z4GrNAEDUI?TZjEtZYl4#ee3FwL-|ao_wHoY$Bhe**fvk24TQ{`x`GZ`B|lYidmM!x zside&Xnk$3+D)bF>{j$~LTa@)q_&*CDamvvOa(aZbQodnlPd0YZGh&GxvX-;l?yB2 zD>1JmNs!d{L5B-CaoJRcVky@5ttj!y0miZNeiC03hv_Y;H|ot@)sLuwF8_eu^+LB+ z?vPL|+}{~y+U#^!_X%X#GuLbrK#k^XR>Tk{RoVb2*dO|V{fHEy#yAS(YBB|Vo5K0$ z!G#$o!^%kF+_Ja)cJYbtWb%*EZ+AkJ&9}E!TN`tpZm;KTr_XIgJEm=HGZZo`j_J-eeNtsWhS48|OI|Q37U^3mq{;Ra$JUHIDFSFtZJT zz9c0sFsrCLy&)#4IZC8;(1NBbAkcOV#f>Bs+s`eZHtz58kCAW>LYb8QKLQlDRXSe* z?ZO^nfqEg+_u3&oyXDMD^;20ttLm%=c2Q-Ph@U!~tWKXMIQZI9QUnaxwNXC52opLM zalv4|B1!NQULb*r?xSxbPqF5@tl*QwSq8;HK#t25fLF`KS(7hIuW*38V5H~FH%^{u3DekTntZi-hDv*um_aSv6p;!cI8&=H^CsY^^?jE zH%W>86@c~L=0*Hg2en++^EO=ISqiz;Z|u3kq~lo~BR8$W=maEYLgGp@U9fOI^yUUF zm1*6ZJ2JWxZf_Jc-r(^(?qn1l#iUdh1aK(%!Ol(3d=K?T=gkM*e(?{4E#!0T^5$fS z_WCWEpPgj%4i!mA&d_>BbTsCEKQ*v+Ga7h;7`q*ar8r7kFz!uph!@LouchN5_2QDi zzlWf-HdIn;s3G#t`@U^IRPX2N8|jd3;y0^#d_vU8lu}OYUg>pd9i5+qC$d-q;3EB? zY@X=Og4TGwYw*FY-3u<9Y~i!_4PrY&*s)+HwUM7pLZG(ICTsU*4_HDBd=)>h(N2=6 zmeK|z>W}$Ad$0iF*V>qQi`)vWWVC)5REUD6QT3CN9g8B%Cs?oCxrGF9E>{$rVP{Cw zSF}l}grueCUwG&f-dTikbn~FZpEKWD?hM9klUq^dhSwdMB-Mk_jH*|Owof(p3gWqy zD%aOnX3DQk16yPymx5TK$m1X06e(m|_*E}7h(+RfOl$*tk!>gMKxuIzg!)E?qeUEz z@$*Lx6m^*gJU3ZLQI68K#OY(~FUeaRpzj&zXw3M0Ou+t}y{$a3pPfi?C)R`{&BFnI z$Hj49GAQjnP9^P)quIBP_l}lT-sL~?73)fl2e4^Od6rl2JvAa%O$y==I@?Z3E;nSv zB!ly*^QK-Na~<_U=@pb-+ulT`R<>x>HTm|Btr|1r+0t2Zw9o3MWa&-rp;chx+xr2K z+Lxo+Pxr|Q2^h5BsgR$C;xh^mdtMrkvh1dBuq4;4UimLDt6* zZ3@y*5xWp}SjoMNq5kaeHo=-zBFh!ZyZZAR8t4J$UG#s*@w;K@5_O@Vur zd7y{YLB}Z9US)t3U%E~#DgWrp*|9$8ocD5_z+W15Zqn9ljg>;uQK{f0#O8D@aZ`hD zW>pj$lSq$00rjRw;+A{oAr|q1262JAO;5G6^hYIvQdDBf8i-mGdf_&^D#u0)$cQdKmT~R^Adlt~RA`1+C zX*#iymy&m_IEl*$JnS`RdV`yHx-15)ez6s7?3jfQ%c!11>Ephr8p{kBjlT}nkIGx< zW&Y)H95ub6^o;C=K~tMrd#D)m(7Fp6t~|ji+74V$Ec&GWQa3!bsB&$v- zYD(B*~xIplr;sAvd3vtRCF+h*q2( ziPTz?9N~!)dhPiLNL6v1?#nBKnn?tx8RAWwU2H6`r3uJc_Z+GD>!&vy8uxT{mGVA2 z$*29%I7C{ZX(u(u22ej{^V@XB@gAqW2wi9W-fjr(DU^J)W|G-J{x$6(Qv6qlYpVgO zwN8SHdNzqwDXqhd^J*6aFcs!r%_=B9ype8CSgQangCmSjX;eOJvrcH)T46)Lg?7|? zE3$w_akgN#Hzj56UuBAxSauK}eWStoK863A+C&$BRA5DPXtGv&z3z{IV zy|6$h`BSEU>|&JNQAT4AGlarDeF`ol@wd(rwkZg$^}-^UhphY!$i%qFw-u;xUPYrj z@cNRb5`0yc=7lQUC2O`0mTnLDDCP(M&QF>K>Nz&s|NX70lmE=U{_C%pGN)PMgC@_p z?42s19OzsITBXTmFB#7ZurvO#JdE?8*Z=03ng2CcklJ<^*aUh2(IEEafi!*t5)AJ= z7suL4UB*kmR7GJ3-AQ{DAWL4Wvn-G@2&fM{H9n5$7fNb(xE<%#Jq%1d06fC!Ht)bF(=>d(bQw46{h1dt+ z5Uz?!h(~`!s6eF8RYTDF^XVY*sZ^v;9XxUQ^a_l@K(t;T?{=Km@C52zi1f^Vx)gT< zl=F8mb-@o?2sI*eP#J93Qek;~W&L##`n7)^LQLivBmg8ZU=dUyt=a`kqo~OXPV;lbWI2+z4U}dIfd{wdfI|?@gnev+*^Z?jG+$1k{{(J=; zFq37up|P>ClT?(pcQWY99msGC4g0LV65H)&xcy|>i;gOOrGPHI8gB9l#7TLNiG#)f zQSY`ZVR53)<^}dW!mG@iYs0}7Fb9(sF5*7h+Xd4VE_5}jm@UEX0;~qVBPcBmNjEfZ zg?lmcW&VI`X^OXGh9q@uazZCw8@}Mb``(^xbBT^obH7X9_Xd$N?@F;ELP>%nB{xrCesqEMF zwY7_SM?w2u-mCyl#1LZ=P67E=Q_l*Ja?9NNv(xZ19d{tqNJV9nwQKj@k#QJel3b-S zjMdZ@l()PI&=rM*v~5}7T_ZD*SCtj2y@Ua22$`^uw#TC?Q*IR~*!W z#YOOAwQXYl5VCPj<0$rdqy&|p?2ZDJj%3%FPTD-~8nXL;;a zP1kd5Q@!~{v_jf(FAEjh((6swiraWgeIGGt{T9|Z6IITh3u9k$yc`CS8pxmG zZ;pvvCG!7*WLjl6HwOxfNN$eZ1wMze?)BNWokhXai82wadew2A(pfxy)Sjn%>G4o! zrJFO4yOQ*!0V#}X_L0H`{VL$>sUW##^6ZTDs7=3Ml@dqeDriU!B5qaY-00qh<`{e0 zDBream=$LMkkzrz_fCrpOQ+vpS^sVb{>?lpJm2@J9EJ%-U&k^Ux!wlJ{t#D%!@mJk z6nLm75Xv-m`0M2v&+flLRS-gvf6}t(TgE_U>_&!m)ravtUN@IeNgMu_Y5UR`>0_!%Iyg07YgalLzm=baZUH(Rtusvs1SiLwm z_j`}r@Rdelr1nw#+ zMT_P&+=F~qaG0?d2h8HVzWz~arRyAA4qovono~f@V$SJk5CP_4Z;fr;nnqBIEQJOg zt)X=Ef@IJZ|L~$P+<6J>O8A9p3CeNOl|yc4*tsD}wS|?HDdEOO0QKbAMo`e+A^+1w zXBbDS$LkoM1mP_`UxicA3koF>_}vgvl;l`F&m&P12~oyYP?_`t&!HJ5;E!&6U+1oX4mDE&f-rdw4lvcGI zri?Uq;MaTDUWZS>2f5Wk3M0I|lWf4uSsaX+E8}>jaW<;zRDW^dY;Bl*z z`1Rvz-BSB9GFDBY8=BW15;2^*2O4!Cr2jqw;V_S~FM2lHS1t$ZHQgCOCwP4y`2DeM zX~hH(9mAxhNnkOIyZ0W1nY(D*ueVGOdu+q-;spO$q%^TZcvWg&z&A8vqZr2P{P`-i zEKvGkpxpt87w{4`7fOI?-@}q%_kEagnOMrRAl@v$KVuGAcCE2OZ2LtfVY?Gh z$OAy01M9kn9jzN+@fBe4bq{`7n$lmsokm0Z|+W=ya@wc!d+6*C); zRd8$0CH&0M#VL`De5G$q{B5qY9h+X)rhNlu9iy;0H*5E}m>X{;^QYN$4iN>(Ph^Q5 z9tjl~tsrdm4{L#$Lin>?WB3=J#&n{fyG=hEal~bzZ*fs`l!@Q$+URtD0hY|9KU&uL zPPV}A+r2h7M^d5>Pxie&`20Qk1>g49<#CcAys8o65VhzvP%oH-i{nCESzfUJ& z)s)Hm@*dy1rP)4-7QjB%V4paorYyVz;DnQeWWFaV8})1R0lx|Sy2+`$JNqQzcR`pL z8f%F;mEm0ioeuiGToWDQj8U(=d6irkX{#K(o=DPNCkh}~E zZjnOIuWx#wb>Z|T2E5)Ev@GHuEyIk8IzohCpfy|yRst*%2Fq&!C#Aex(TtYZLBrmW zPa(sgb-$P&CZYjo3sBMwM(I!&ftR-Iqtpa-zDn>Jv^O`<8g`k`gRC;*UH^D{HS@(r%X>;@!ljy$?W|;qVU>G<= zOKVE3*Oa~=A6%u|rxVEJdsBf(4Me*R2Qmpg2NS1iq!pl!TJg?l05YGrmRNtlqO=2v zL{W_!W}}?SkOBD*&)EzwEFO?sooTzydzMW zQ^<0sHseQwODOM23u7?WP@vI>`aJNWI*6{a7Wdv$|v2Dv~O{EYr)_(ekQL^zY`qkw;VpE(!4SJcctYtI!Bq) zH<&CFvDdHqQ@eDw(E13Z#tdEiq69fi0x}F&8yXghj@x@(o1gA4D+!gj7#A`Z8+WPm zslF$VNv`SDjvN!r*W(sDu#-enN!{M{h+~2)8OAf6XG_O#Zmi5KvK>_SY(vuq(8EhB zTF@xe=9=uoldv6?f9-3dx!*IgojNCBR>-Cv`{Q3I1~M~PwoF;&NWo{YuIi_Ru`o;# zH#e*7PLYP7b>3W6op%{eBg`~20?-Tsr%)Df?$lSS)IjPEOOW?EOl`~UPdCHEkG~@Y zXjzOIjqvMm07=WKxij?fnXShhJn*lyJ4^BQ)Tpc~umP?9`hV2x&MqAMaHjGOZdD)w zaXPYNsqA^2)-(x|$%|;@+CFfW4a#2k&Twgb%Sll3p-7)WN>UQ9!2e{uP9VYs{v%Y(?y%nHI;urB;qVgPKBDNf-47`^?j4`t^;u8Qp+8dRjXh( z9!G!?b;}i4qew^a0achE!uH`n`B0fabUjrkr{9L{5<8MyL*O!GKy%2i@VtfLVQaVnu9kjEt=QBOP)nrH^tb~ z(~~Uj9fod;X`_i;9& zL=zccuzL`gvDy&?&^1QKAv2l4w8Hf3Ehx$|B}dVzD+=2bNc6KIFA}jWd@`tCL~<#l zdt{3>!YMGvV|m=4ih)y@uN1&XlxzGpyf`gyS^lwNU8`03 zzY~al;IyX~8n{+X8O+|DK{*tZN;ib1fZwTW@ElUpreGF0Om06K-bC*0*O5V(RJx3Y zfPEN$!ecdu?w-Am-g|X6hRDtgLq7Orm{2(a^tHkLlxj>_DzP`6bBP;Nfp#*BOUQ7rQ5(;yCc?hwf(bX-1mClLr1L04$ zlS@f8=_(Xnc*TqF->1_K^_VM=E8KOqR}4b{w?dZ40RlISeSnOiy*l&jTTPSp?bEqF z^;HZm%o-jyYjCb9B59DN;fFBtv08t+ocw;+Zi_B3Kc+MOPd~PQ-*zkXC6~Y4;kW`V z@vAV5XuUHDrP7OX-bfi`ZT|^~I|yU}tw=v)p`pVkM1ybZzN|m*gTfjB+XG+_D|7SG z-~2V>?bVLkY2(JgR=6lzDC9B@ib0BbH=OWr1onPoPKr{*Ywz8>(xIkTKwvFdXy97( zc$nsDJ;j3-^T?IAC3+461DT7rx3w+d}rAhiso#IP%fCQqMdI-KPNPJ zp_o!HkaK=GgQo>c+;8@MNWe?qO&E#=AcitrE6tOzMM%ik|5z_q=5cz2TLu_v~if*J)n=ODLD-jHzH!B{Um_>DA za(y}{7^xIM=n{aiIWM)z$wA;NgL@VJWgNOezZhs1x|S4_SBe{e6H08lYymlZxpW$2 zuK4pt5Fo%)w%~{wi~}Hb6lLR5-Uj`C(FNLbfJ4gVEhMhnZh$M5dvQNHe~leN`M_Ev zFW-l=bb2j*0c zo(?>Ly!@YH_Vn>&yRmR)Z67M+129a0-c1xKg9TRaP8rmMYi)6k()K|ENm)n z_s4M(3rz19M67hfpNlM;Y^p53SVT$)%>`R5h>>T>p6RZEYXn>Sr2mnqGWqXCmC-^U zShL76`a41S<_$~=rzz;V1KYlVWLIS9zL^R8*4e?u$H!;VDN&uy)$1m1(~%nK z%(RhN40PLcW{U9n_g{%V}*mz>>x(Z@X#C( z;^K)Xz|OMr`%&3}b0WwN!)Y3f%i$RU|ADzerM}yO;^cbI<>u&Ea42+YS#j-56$qD{WY+OG&Z}9furK0Ek9J6!h_+kjLRB zoovguZ0v$2FbwxR2<2p8w86v@o~Hrdzjc&HoN0o^#=@CJXJHD&_B%Ul$8@>M1x?Ho z{S4Ut3>KCE2{g0z{KE)Fv`%dU^G|NaVt_y$K>eIzCIbbNWk$PM!C0?_OxQ zzwR2`0Mk*yr{d}VXcsH@VI0BaBT7_UuQa;{9^z7jMrEk7CaRjm6OR*kj0nlo@ zZlRGZ;Xc=P&USau)W7uq4diw8;6ncuk?4Y7XaOz{S0x(EAl5OOFb{0>ubRVrN_sd2 z9gbKGft|IU0m^YWSI-TNLUNT+lERFc04y72S^R=q4}_9Hp$`Kb$@HxS#7S*eFW~zw z``hPmtL4Zv;uS*G99|D16I+6=>avKt&ql`qBWic6XodA&@GAVcz8elaLKPSX~B# zs}Sb4Xq)_3cBm~>enGpg%W%*B%bp+r^+n4v0%*fcJv}{~RMsdhf6Dw#5O?NB*Q~|7 zeQ;7~c46Rt>ftSaS)(aqoyYX&3|#$sl)QzQc*Bo@5+4X$;y5Px6CbyDa{Z~%t+abe z2ky#&8qXu1eAl2pbId?nD!Ei+ks|s_lu6<2e7gS_r^#xva%1Ob&d$o?JP6(eWP68P z9JO`EPaEs$^?!ZWtiq1#Ghpnz_qRqr!gSjim;l2LEo7w8b^+1P z;0_r>Lr^H9-m9iMDhr44Y*skXP*cO8^e~Z+rfKKdP{o(+G>qV*pTQaomvDE;oE@x! zdNCj9=yNVvHI5a95Dei!G_J}(V6eOh<-jP;b70$9Un%rn$Ag%JIBgzFU$Kf0nkG0I z&F!M~FhhqMypGc#e~#N6xs>5U z()bV#1B*WR;$JJ6D&v|=$sZ5<5i~|m8dW)TGF=8IUW^3-_qa+Z-B}Cwf4!%v*1lgn zApshh^Dm|V+djyludEIZzBq}*03K;V1;4*!Id0jI0noR0lbeif&Lj+$E(haj>qR92u8BG2A zLoENo3eEPpv_J*;F~Lki>34l-|GQLun+kLvfJdE@@?LoXi2f$1;tLj*zR>KPTcuWa z+-Tss%E$xMFR(s>h8R&s1`V#cB7ak>!YYr52*BPTs73K!f~m=?u!L3B2#lHoi#sD-zzcayr z!qRKsT5-?{{^5k`6M?7YX73>6)OmD*k5GOLLNXIeD0Fi)d0=bSeHbdfkL5 z;B@*dD%7plgIT3Qy`=w5rH<@^PdXF743{VKsT@AzFgnu)%`EX&E|ECLEnsFkkk_c} z<5g-FdRV)GuqC)*rXa5blF7qx7Q{24;<5l`wbvy7m$;${nVH;#*su5e!`nR)I2#Mr z?)el%OGy)AHH#EMTF-2_Vktnp(GCT7dVkg`PZ`N_k2nIoa!8-*KRF5Gzcd2>s zyDH?RwP>hAIx;Rtz{w>{{r^vUZyt{IzW$FYt4XDa6jE!oR%NCr%2=y`DDyl^G8B>` zL`8!kO@^eX%yXg4icl0~ZX$$G#>f=sbw9QCUVE?g-M{a-e&?L)`km|RkGq)iuG0!>zlrBArJ)x zmYLs5Ss7F>+)&at=}AJK>nWb}h_mSz?3F~*xZuA9c`g+d?1Q0I6lfG*>-5QwJ7s;q zABduiUPoIFh@i5usNt6EW=Z zk0(0k!TdD%9}o|Ff=xxwQoc6RhtJMpDFPt5&(O? zj(N>os9}_jh`m6D&ylWdTDshbW`0@VG1Y(%uN8<%LeD$kRVKf|d5PU3Q_Hh3`tD5q z3~SB)UJbP`(A4P@I8SanpH4FRpoh}_+Jo;+U7X&J*Ru|{UN9R~PxUhN!o>y9(p=-Z zN?Lb*!QkVh8CFUA<8R?v*aOj%C_V=yB+fDeeCZ-L&Nh<=k!~=@nlGmF^RIsdT?eu& zfy-spd?Y1S`FS#evzJex?)4)_3!x!hjna58TBpdCDEC?Y^R#AA_FN>qq=03@gJZ%D zD-!&FxAFK?V<1_K=nf>cms2l(h>2hR`xA@PHZn5GhqG6+{-0}wb6mr$4gQ^o*Xg#ZAfTR9RwVB{SOEJIlg+j7@8>}sb0KL%9e}D9gKS>7<#W2a0V)mx zDbHT;#kXbz{PSDEHkSbV;RNMz$PGb+4q!2gH|6*H6VydUx^uXh+B@-6HEyJ1X38+m z2#_BEh*pe}kqzO+_6YjschRGkjNF0!epK5^it+U ziGCU$W&EY@nu6G2PkRRfBxcVI2MRD!UogoIMU*c&yi!w97h%!3CMLW~U>D<=7JqH# zuT0_%*@sphXsw;BO!bkm$?IGAF#8TpMeL!vPP;L{)4D>^V^C#(ST9ab1Se22RdQIG z{$wL2UksVg>e~*{O+NkmvN}6cbsKR|$G?2MwDKuca4&Q0zkGbzeH=;%q;E3*aB*$_ zIkSVW9$yemiu@%|pXk@;U^zduhW{Cmzhr#g1;_ z=;+XVW&>=}sdo*2FTdQ6RWR(?M{F6PZ8+RiMGpd9XT&w$oe444XHo*U0_?niZZ{?S z81}knAl&Cz?N}*tBhNNeKHjtCGaFTx#px(sS2sBwM^0BULjGEy(epzN+~fJ1-m1Jq zh$YiQ#qaOdosJg(bCfI@7(IWbj1*Z(X-T1w*-7D}I5^0Rk~}a@=VsH>*Z)Q}OR3Xxlx|63Z8K)Z{0Dtf{UcIgJeX2fF?fFwgL z#da{K=76JULE&s%tz{FAcVK<03knx}lD^D^P|ZjYHQC=wVox}(jDLsdfM0ME3_+xQ z<}276EM$l#BKaJ_P*5j@xPD8DKk}d(!j5p))zOP%|4x(LiSDGbd0)2v#uaufGu1n9 zh*h+FdX+{nP(>-OUzO{c+O!Tl^|%*Czwd$n zR$_tbzZ|tX&bKzSC&cjZjX%)tBRBT-JJB<|^SX}SzR8N8yu3X>A;;^TS_k#l7z#j_ z=Xc7pgCp7luz5lTplE?;03Gy z4gGf34y6#4(f^g;!`y*H5A0dDeO$vW=9%F<8LL6rmsikB&KDDb2cTqUJlOX=yqr<L8Ow3ygFYvs{#_+z|!Rkt=E!0J_5o*ZA3CM+&DH(+33K+3(}%IX^XUAz#D>7f4W ziM%<>x2k+&3Ijck%^c{YRy0EEhUoq#<#ALOj~M_J)Z{3%rs&#n4Gh3miHMF(W_&O< zd-4Xh9@bJ$MgpWWl<1BNvX=_#J*JpHc$LZrDI9@3Ijy8vB^P{fb7FehP*p%8>a6|}H~egQ#Bs3xeqjBRe1}hNYz&R^pQYmeU5hTCfdkvpkY!A(?O^rsII;Ot z(fzYko{O!T7W=4f`?wsJ-Sesv=GYxjZhHf++`H)c=ITU z3wE?uo)}L1UbkDDaM^u$a>?OyM*R+0Ozxl=99>hrl|59Gdhp!vzxR?|4Cwt^p%SB> z>E4Q`?OthpkEjW-n?bKU6>~t{Y$+~4Kn3+GPCZ6R)qj-uv5Ut3rNYOfipYNLOx_kL z+{)V@JAkLvh5 zfgF_Qrp>@9S5PWjB68zAtt0;c4Qg+;N9OD}hC~7jp({PpmCGY7@M*?o#V7a&PtPM6 z91r}@xi@OM?KGoqo`R|`ZxK`tOa7&*35W?fE+zE6s4)RQVU9AHkP2tyM-PZB$9aE& z)-$C#l#QdiRJnmijr=MLwEQQs%A+i*?$I7KK*^Ew;qq=W<@%t+DMc}tUG_$ynS8}u zq)lbe2UxD6z2LYjEw*7aRZobRm0Hx0T2{>mWWo6&^Cr@4><_sWE4S~EJzg;OJYG=g zm^&#NiG_?38YG{I#W0fKtltrb({+@qm$K&Dwl2VQ4@G(7%g29Fwe*XbGDKG8eyQ4RPFD-Lj`lzyNNmU9ZT!+|+de~YRykO; z-bxqWYMqRu*cEN|;9ynR?WNH~Zfe%Of^h2&GSm3esR&UX?$zU~bw1V!wR{U;27h7; z2|U$UZX)w8a1`rpGla3>-3HiMK z1k%%d$%(fYsRz#;uLT1e;isMfJIN2E*l&|;>5?-YwW^*9 z6ZSGd#2Xv_R6ztr+o7y*pe7UNFFP4Wo`uU6m^BjhNrWn`MzVog<@pL_$C_O$K3y!f zrkMZ(iZ#ZOVV<;-1NTkn3lo1^z-CN!w`&~9O^I}?X~y^d!PoqYl|s~^V0o?PVqg#ZFkKx_tE34NmACN13#bi{rnEPzBZs9q9B>Y%F>5J9p5XQG$6BhRdC zi_7}x3j*goIfaKlxuSsK=fN~7m+#CCV=Nn~)@N`STs&REHipRd)k3*_g;IHmiHT*K z;{-Z^^{k}O5=!!(A|w#Q;ezesxy>DgS)@ z3k}dH%OyVNB@XD^rrv_7e7kuWdOuu{c(U2Sg?G)=bcTTqN>LUhxiC1ot9VS3 zs|1!VrVyxSmBgsR!i7r{n!}|l-TLIEARZt(`A*elh$D@=mVd*o!-=!Rrl*)XfTWsh z1wfMz_ZDH_2tyH<>C?$!U@ylXNEN z^2@3ELQqifrW@-m6r(ze3m--HHS)eU2&!>h?|u5lW~Z`8(e@X!Z&wVjQZl?r(^$wi zWk&wkWAh^C?#$MIPZG^)b?x_U|Fx4x?LilXs+N60ww`HJ0P1Ia#a9#DNC6%^y>>ve zl0sxq9ZiHOx}6{_qPHi`(s=C6SB$2~uQ3sF>Ka8r+j0s5c0;f)f1WJ)ed(|wKa6IV z{0Nj3r8u{=B#tOG@2^4$871V1TdUn+xw>v`x*KP~!Y&`ATp1xbNAf39rQ|zDJ=%E% zI$KeMqOSaK)2nlOx)kq-zTQaiOkL$^rtyZk>8S)o8d|^G&v?tRPbm;h2lkV|3u17h zJ*(tR+-&Il9I9 z)>TWsf@_=0!jOXJl7(F?$OvQXI|%O`9r(F#X|f(pgR<72CQCggO8I&#>2fW~t8u*h z!uRe%Mc{UE&puX7FJ$||9)``=PlZ>nqbM);TbiSXC$*GSZEfvS(b;5mG?*MD8Qmuo zpqlN3HjGMsGf9d6ldGzXISm~$-lidN@s!87e%XmZVQpXf0P@s{uu~7w*tYYWw8~7P zntKoIAS7gnV(PV&9oJ=@H-$l@o51-!C>#HRu!;{x?z|1VEw;nETK;xJALUZenA3pe zI!b&e^^A;o&~W$wfs_qc8xqefB)Q-85kQtO$1`F^El7OOcJa6s9e-t7Gfujj;Z*z+ zI6)H8+8*f!G~*U^D|jG3Cmo3c;66r(9(0{XZC|P8W%BI$Bpo84Yb2(S*Ud7m`7%p2 zi^VQ=k&x+yHP!ZHj7f4=EIJ!?QF#6od1-&g_y6Vl9^eU4m?id%h>L1rwuVdBkQY4 zGcoBOMzNZwWV&MS1ZC~@?q>$GT1pvyQ7&;iO}lhM8aE8BL*PS~n71$8HZMoX9z_5z zBY=qV4!M<<9#`IKsIsqmb$ z+qid~_^Yoibf&qX>WVE#1va~bXU)1l@I4rU;|XQ!&2`DXb326LlZf$c|A8!W2&H$n z=zj`};;iz{uBg`QS?I`Vr(tnzsquw?ptRDC`YUBeL7FF=*!ce-p&n@S*lSK;iYpw? z6Zha}gVT7s``D5Bd+J-Dgt~XnXY)jHa%=aezj%!6u2SI@+$7cNx4~yJDG!X0B!Xg_ z80$s>7fZL)q{-cawitVREXC&*O1|@#-_K-Qzh*c8T)mK+lau*Uac&=hk3)|WRr+gZ zrn*%X>F|0W0>=O%PICt*#mKG7s%x1GGm>7oiug-srq5M|9+v;xmUnY5mH9>I%%`xv zgudIpLOy~I5;^yvAf|3>o$zZ67`i&ij@4l8@|t* z_h~Mq9R{fKy%~WL+fj~k9uKQ) zTbEa}t;+`(VgM~BL)W5b-3 zSQ^!Ob14{^QNvyf?_!CWtH#pBB7RX@BilYQ$F+KU5BQY;NRAq-59(HVZ>SZ<+|Czj z!pZ#c_NH9leIi}^YV|xlx>K(H#00v^=1;27(@~-Dmr(_gwb_WJHK@*9D6ny&=UBn@ z(xKgL^2odp+h`IW8yw$>kbz^@EG1#db9|2ZXcr%3WFkmu%~w99)|HJ!uUA(Ot3R#Q z=o!Rcar6@&OjhVlPJVk_5|Ry4VBH7qQ3_e|7*Q=BUWm-_r;dP*_0c>aq}(X(ZKKx5 z#aZqP4T^V-Tz_YVm42MrdpM{=x6B3>*tF@K-n)&lSEOgpeHZkPOb)Axz#&j3?e-#0 zE=Ga&A_~mWYd{XugSqIeCUq7Ic({I31RKXb_;R+WQd_rS#Li=gLT;C7@F}? z-k7j^TEl`fF7Zk!^^)?dz2H^Mt!Zkh494T@7k#(PyCiLsTu8)$W8by1Ge^Ng(a_V( zzQVcu-k#-Euoa9TJnZVWLQ0Ie=>=}(ou1^5SXR2j;ymcgXG79VBZyT^A>r)J91k$c zR-1F~B3gz_);@^eoq86)-k)UW&M&LAo%6cA)pl0(_Ycxt2wgiNu$Oa#$PBN}ySLRw zGR0`5Ub4z;C;1zRv$DViwh28VVI2Zl_OScZKJ^rF`YibNT^|k1rjgG+E>Kd7>2wluO9Pkrqhn5Ls6S}Iy0?vcLj#9+ zmN}uUid(lt?-|4!gG_O}^9&5)4~AtM1VFg)^{|pW@t640h}o=bCZ@m~K>?8$5R~=v zS(mt+ew+bpTC=ERdu${twV-$RqpaT50aBM^Ql6+Rrp`GG<2ts} z{IJyv_FnpgAhPYjlBC(GR^F7fUV6SL_JWH3cbR=Z$=A52(`|GURgDf}kOsf}sVpS> z)8W4)e+Y`((!02Wb34IB3?EK+(BeDzubKnMX4ISqu~We$GE{XJv92L%mE;-}+=58K z57v2=sMKzRlX158tvO1E(C;&2t6BkU1sxrO}^Loo;R|to_U= zW*Q)(49JrWF=(DJ#=CNo|1Jw01_s#C0qx>a3Wg#(=^$Wx{&Xn2RP+^{2`=J?vY1ncJMV+hY}wnP9I)1 z3J|XtaSuyj5pV$tCzvH>#ZcNwZ#nu&=g1gYQqAiR$b*1fd z;}5MK&6K~heLTb7G~Gy|pll+&?(vlV{?juC%})<_YX)62Ofgc5YZ57Fbbge3K+DL; z;@WcHt;>fvzCCqK8#OMz9mRh=*XVReTH|re3+4_No_2i5(S9wvcSiS^-p%+RhJ!w6 zdpP+YuEuWdeIzz)!ZPMVIxPy6CC&B3*(wdAFKC?^KHn4qworhpn&7lisjh`bXYn01 z8@7K9t19SPp~$47UIYA(^Dt2gjV=V&wnASauz5!_yy=%qe}Q}9gW^F~OamY7X*|-= z3zz;%%+Sh>d2{dI1bzHnHGkOn5S#6+zZ z7N0r))9IOWIYn6p=A7Sk>9$zZY_*_ovK#hon)|gXXvWcAm!=NgincIEI9GccUv25w zG#9X~;KB9G5W8EyZqVYW2`L z=FrddVikK=`%b=}B^%o}N;=?#XWk@lsGY;G!$++^-E#|V61>54n$ew?zv8KO-GRr_ z6mb|+{Fc!%;?J^BNcY~a^DsA6`T{RqVe!n~rvoeTLTrVIG{}Nu)D@TCFP5A` z$Ost_2;9Fn1oCGt?Ar@4dbf?UoU&Vs;1Vf>R!S#mjaWEd+?!An-eo$Ckew8w8dyvc z6R|SUnd|AbPV4E9QPX2$NN1;rZ9{4wo^Pay5p3c`2!ko2UJkpC6SfBD&^bDSHa4+-8`AHx}5yU&e7dC-b5&=Y+iqq%?Oh6)7Zz>uZAN=uB@iSI4 z&a(mZdmM=P;z`bG55*A4(|y1&BbiL0qpRZfLYLMUF<|z-j}&sTI4VZa4LzuHhe3vm zKo$&9=YXVr37zwOC{`ln+K^xqyhEhKojyC?+n4br=)PFP0H(C6!)QD-TP;4VZlA}N znWJsnxdBZc6p3R6XQ46uxjH`~^`>w@?6YIjBNUf6Bso6W`bLft`si!@?|+0|{m;Mt zH$RQY2xc!|4q;Xb10$;wd15I^QUc&l5qiIkv0-FTVq<4d>bHcZ#wA?az<`^EAf=)o zIRnM=SxBAEAY$PR5dGB4$s{z#*G8ozl?W`~gclh$^&&9dz5oF70v){wkUtSPkWt7A zM-DO8)Osoca=W<@B%XGt*oVCt6*enOP^=`~mjHcRgifpo%drpxsS2StC;|;`-25Yx z$-_JBy3e8yPZQ z1Q1%fazEmb22ptZ!w8xQr8~^6xu-=lKDNvJ_@P7v`Su5HAn>aXqac2olXv7B3gS(% zZ;iioY12t|3p^nz3)bcw#3duXu9GvM0L6_Lwxss!ejF-XN2ayea=hk>wKqf^N1SxhC6Sa@^0#E;jtO*}PkAq55qg0a7NY@86QASf$zQU+SW zPVVG(_#Kg3?$zfW(YTE-&`*Frly1g<%%>`m+>vXxoM782(7t4+7`4u(AY&xoz}6PD zLqtgbz5txI8+r!`3Q0G}b#@?gQN{4cgw7r@h~08KxYgq}BI--}W{y)%YG%(-$#Jo< zfh(y)xA#`HUQ-@sDYtIV->BRkuwW?onLz$|< z>-G<<*Ep7QL`Je+BX^zH_3_3s8|xQ*0uAw+J>r7bu2htYyM#~lj7qiDEUJ!9((8SG z<9gSNu;VGJcT!o)Xq zpCqn4bS~+P*kvkw*!j0Y*X5I zQl`5>l|6V!*suw&=r2Rg$o#V0Q*8mHtom>xCUSki0;bg8inFSOvOnHVg@htz^kr9F z64z1adhKa?>+_n08-xk%y9L?6EeLJn%r&2lE!-gRVM)gc8SnvsTRvSZBj~n>25JNe z=-!j>06%7Y1`VQs(~Ecpa=E@*3Luu(vvc+iTpU5^LL z*Yd2Ek=>typq#pyH#S`4uXyPU-NhLc-O-hYeU}OrqBPDc8C_XFoaG<>T>P6K*CwIf zK_6dV_4}cSrrYp*bXiAiFHjjmV)6{}tTWUuIxgh-w_0NNrhR23ZP7d0+vtAh!jiL2 z7tp6tKJW9LI(&_6jH$Mq=eM1iHwPTZ#2QH%riVdGnNJWEN+abM7j^z=@|6+xZVF9R zGa!W7Wbl9e6-?7y>djw6hIRHV%^>W|sX@W90%b-ZDR+~qI{6wS0@_Mgb!jTwR)h%_ zVrea*;pB7z_n~Eo_G9f{eA=^Va`-_JbVh|FktP8_ANW~s@;JF`3w%4I74I=M*$nPs z4oVFtmWkq37TXN~?Kh*fWF?_{5spWbsBvn}QsQVqM(hyP=DUY+^rrNAY8o2<;3QnZpZ3$#k!H)8j$NJe0gbolZv6@puuGfw+%cH< zk_Nd~Lc+qvErq(a5Y^I~dqe$GqRW;QTn9PjK;QkuxlwdgR(`3@JNR!y^N*FNo)fiS zc-sGA$@$fX%@_7zu$bb(T%fVhL;Yz>WgQ)UHT`6p`m@=rvqS9NzRS4%R$nu}F+edZ zNgiEsF;%gh(D?{DEjG-P!G{>@!?M2kfK|ZfhHc-VX_LTWb z%bG739&ntqgXZo~K82$E66WJ~OI-vu&Ezm|6&^k@g}D4njuQ)JxzcvhvRF$@XR0;IU39Z|zw^Z8k_~*1UrOKIrb!)5%p#9|S3MUZ*{c1AiM+HJ zFA+OMSPbVQjxC0P9L)ZA!pUsi zu$*~m|CW8+lItX9bJ=xPMa6BiJc4U5c*d;VTP#aDxrlGdjq~T0fqCV?o!?167?{jxIpb2GKFS7QUwF(Z;GtPV9EQ7zreN5L-$#{te z-)&IUahq(AmA7smaHs_|aFYvXTM>?x%bP8WAJQ4CeZLYOR}Oq@;Hjf+%$5!{?*Nun zwT8I=yx-&8pldO!_T9wHJhzK|uq7zHT4^RHRIy+Ll&!gc2m6E4j?##&G7j#!ohz9y zgSd$68!B~_1omD?NLS6zpMY^JKpmKD@A5F7;Og|ZpeG0N#iF`fH5PLH63u_XvA#Y@ ze-m8cKUsa9D=vC}8-ZJsbp{Fgx zEU!j&=Ze7TR&5>2%5zYjgJRaUfE zcvN@2W<~bnhnDkLOFUaqt^F)9J1{YQiF|pGAYE%K(@Jd5NyJ!%b$r2*FrV7B>YBv~ zdw1UD#l^;y8pZZyHX6c>0TOn`+;JQobA0bOn1v3}R=281B}1=Oct~wCnIAJDDXf-y z>9D6mV%8C*;^jNra52rYsxgU!Q$qUrS2y&iXHiNErDK3f)LeI5OjWHTZwJQh6dWF# zE19lTe>L@Ct?V2WT3>@?ep|P0E{Bj(xp>rP{~Ii~VRA^+c5$e9*l_aax@``RO}Gzo zGoPl60!4Q2ol>QeA2-$Z-;ppA4c9pv6m4i^2wqHWmKO}R-OUpursxQY`3-Ru=sLColl+zk@T$G(2?C!YYs9fy3H7%<*DK~m%QsF5K z8!@WBEZOO1aOLaM-kX)xd`^!&x?XE`ZpT z&M)p7P_|dzB515P?0o$BYf~3h)mTln#d>TVL5q~*G%ed)l5`e@ChOcxla{e5@ffx; z$dwWk(tY)FYVP*!+f9!?4_rO^K-g$0=Y-|tz(6~D?d(D8A-{mG&Sw3lF)adh zUvYFvtfxB&^;49I3HRM4JKG++T;IKuGidMU2UPaRnZZJpp9pkX}!Mxov?jc0OCa&i+`(u*&oSV9{@?l!rsC#Tw5U zUq^f34T3x-Yev1L&_mqYf&>{474pl z)338I;#X*>Mq#6dZEVs!3C)GI*y( z`<7`cQS(04Novfvo_%l?0;V3WS%j@`T^pZKF*mDYrD zt&SGF;CMbLrD$UZHB1{hsAA_67&h}AWKBKa9}fYxjPXeMp=Z07VZ-s1|Ar* z=v9`E64#4NKD*ZkG?2ZsBO)ZEM2FhATH|lu4lf0IxWdBW&~hBc~$7IcKH8>|4)d{X+A?b27%gzt3C>gv!%JOtKnD7b_!dTr(7Km?& z%3*#|Gv_RTz1O~&T{}{erPa+(u9%oetK=6F$4Af$@9zA)oKZrTNbcTrzj|9@ky*zX z2&jUSnoy9b$G1pux&Ts_bds}J!2kgl0R)UFG-DFJaNh-r5OwS45}_g&B8Wm|Iah3A z5a;YD+b9*#`MY_9aHFRz=JXU1nQs4h(asN7SE!E_N0`n_FV6bbGGQb--zw~l-O&4o zuWOPGx6BZ*L*3km;AJnW3}$efk5w+q)i<2SQsQaiA}XQ7xytX+bEg@zcUK|<6_%{3 zT&sR5)T?=o9rGu&q3$V!5;H{YYf0rxfSBbwMv7TyI@BDVNG!fu7zNxU_(V(YNzypI z8Y782MRU}yZ7W_+lM8CHsEbxEHE`#7+|c|U`j)wr4M*{JdMi7A^}Dg60l4JSH19oR?cbIM_|a@k`$ zn517#I{fPJX_-CLPiKIPNLadu35!It`rlx0`PdS&Ml4$VLg_jA$022zx$OK}k$V<7 zq#x#9{tl4M)v6KH{L0fxCHO|61X-8+H8tRxg6H(Vw3jOEwq5s6+KpkoYPdwnXHthT zW2j}Q-(Nn3R)fm&Yp}L68lP|F>V?pd>@2y2%3_MX} z_3V>dH@={Yo4z7m>=OMk{$}|v#Kq9pfBRo*HoM{6QyDp)FJ-nneEP#R8&dTxQ-ts< zYJC=YF@6`*#h<7D&%gfLpTUe3s|W<5fWB=2BDbMLXFO~4<|}|i+;6m?`E$*n^XHPq zqXFl^Hte#kKq?BS&Ej4Naq-PmNc?jv$uhQ%k58|)5-uH`^V&b2a(9f@Iu+s%KLQq` z>0`7j08;HFJwN3|6Yp0whGr-w4F^Gi^jpZ*d1D^{8jX7Ro}hY&mw0ICmI&mAebBCX z!?5q;YB%m?e0k1SO`TQmMNu*C=!Olb6t`(;O zO_fYc3!{uovP9j2lhqVzCO-WsXfU?8b?Y~=kUwj4o#2DFm%ol`8#rv2fVz;szwG-8 zi)cZk_YrctZD8`MTA{xx5Rh0#^BImHu7!+Trht>W1-kJ%^GN%kjok#0&vk^lJ01ii zv<8y7j=oZP+4gygL{EibG7Kq`8ekZAydRFNE2~lTbapikAV01Kq3UUhJ^cMgOdNTi zu-$nkP0#pUeyx^%R2w85d~SXXBV1?>iP0@Y=;x(0>FABpU|6@prmHSD zK+FqSiHdMSuHb=ZsAhH^V7|I>XA+_2oB&#D3P7Ub%__>SB^t&0J(B8v;Gp-;3?miB z@jg1L!?!USrNjL>`2<46tS<9ZzRNb$F*ueJ=FaIps?c!!<=W2vXKP%&EyNw0uCEy^ zJv+9$J+ibqlqc+~+x6$)EMpCGB?J<5WuMvJJ2q((I#g0-6Q5(aIXujU&xkGbxw~DP zxcC9~rM9(vUqgeQDRi`k=I%Z5)$Qitu5(a&CVg$MP1)H7C+^Sj&BVwkBP^{NlF6lyU=aMpvR#3P8+i_N*J$ zEou8FEW=BR%eS>&W|tfn;NxRSv#Hw?z^KZvUj6WCLrUz95N*e{`djVg!Dj+pQ>?Z8 z^-LZ7IVI~=Z`usVjJ_)$?ugUSj4hx03R?fb*V<5s&I1OzcZK*`%42e=SggkGZ=0V1+wKIT9M0GnTJ< zTSF0Slw4dB@d->pS4MVrW?pW?h9P+JX2T7oz`Lh4wh#pF8Lgzlr`7RuiCwS_Dat== zSmX$E<1JWgtiHh>yqq#`#4h(?9*k~B`O+9z*SL=odh*yv-Gh8~%7#9Y zNH(M1LGxKucE6=KWDT3`zy0QtU8dzI+P8FP2c>z&-3_t3P}ra@HnR&UIT`X69%v++ zAuE|nLZ?g!kBYW)7^Xk!I0nQb#whM?jE$0dnyBK=rO2<}s4>T#Wxb8yLyvbiwy=0R zb&n3r7G9!~I`e2Zcf1c@y-e({Nc~q{y^f+W2@$w@H!2sfY?Nes6Z}F1xoM3^0Oxle zp*UxhgPnSOd? zrNX(e?0_Fq$MRL%=!rSaxMr4~uFByW)UKYL4^dLL;r@B;D^ST=Ew4Ungz1v(G6l;_ z1Y|jW!MEEiO?&ve=v*Zk;125w{rjncwaAy8xmy~5b2CCmSc;2_t@vpE%UKkiIP$0y zcmNXQFHkS)RNqeNI}I20n>>xe&dCH3@E7$ZialfB)5{{z&1Y;pgdZMBK^Ax!qGKGq6n`j!o^ic zgP;f<4f`yb(MbtT=)gj!U#3hOMj{{4)hMtRL>~Qk=Exes8+-RT-8HukV>M^cJ}pvy zfPh%>QOt!${n_{g!CrzK!baSf<#HXZyKK;s=i#!ly*66-qbO|di*rxi=falkH*KG) zKbG$;tn-yo2(aAvieDh%+IXYbHCdS&*5; z8doQ7(lSzi_ql5HgMoVg?H9H^uKX!+@(HOX30hh8>HGFo9hY&(->GrKqu&_ zYzKZc?%UEg2ks^{K*A8_-ZDI3cKjuc)OM2be`RWRw))TKpr*2_g2GJ^>yzg$y^`SU zL#4L+&ahcPu=xeaH4_sZvXu(Tj%bz7l7X4B;1hltN4OO`4|b~)Pr^j`vePGN_!G5^ zMR@#$nD$<^tB_cl^ys^$SwJ+j%EW_)H$%8=8@qTc@sl8vN@`EV=Ub1nhumDeg6FK$ zVa{=4)!a_)&*f##zIO)6;Bu9HO%u1bgZ24kvA!!dY7nxzSqatqy1;G-8-on>Hi?^&CD5FPKo#wHBrv~moyt9^Uva#D#^i<7s56AbR-FuNJ(9E7ctjog)hG`(bD9n)=J5N9*<{o!6?| zg=0=db9oQyAi2P$9rgiq>=GPt>{Ek7Ub)|+TpXiyBB@&KgTLb*E@Z(71=vN0J*MvluaVIPf0)<7^iI z%~C|n3rJ7;5d7tR`}XY&9uKGknlV9n8_Ud~O}K9DLBm8!_O`AoUG{|DG%L$-R%*Ri z7?lyIjivuw!vD3brScj_AI!Nd811q@RyS>bEa*ZBEaU;6lnH|`bo597XIA5@LL`Xr z3OndfsNyw6ejPy>rtcq~?4Bn!9XAC#qZiX~L9WkSgm5pQ&YNtrg*2WL_0?ipiYHLG z#jJXF<{5{Mfk9ZA?v;UQwEoUupr;q22BR;X#RRT7C^3|~&Go96Nl-8(e={H55&;Z( z%kv`w8&ASw8QBIzE}^9R&CWW#h==d;&?Cxc)C{&Q(jQ}E+Ia2cyUp6yv>*KFPcen^ zNqR4z5-mCAgk4;A>JR4s{$rBrs%?yCjnD0W{`G&>XTY*qF4MWc#r_A=dsXaCdXxPnOkOF(tPNM{# zK(}JN3-8^G_^1IkThBg}!7NmllMII;C!YKgBkN6<6C@rhOwsRH{n|X{vwDUs;6eMs zIcvQTtrl|+HpSC4z&z@n`9gaqgg-4H@ej4B6k5PCFCS|5X3R2}2k3`|%sx8#ZZ5wW zi{soh{qC2RLb3eYZ?_%_7-owhiBsSi#1XkTzN{XbxZsRqp%aa)k0e*A*Q-NV`Bu~+ z)lVLwI`g8SIg2+qA}{awtz?lG9>LyHfgx~PtD_eC%NI(tq~=P5_7iC=Uqsf&2WqxpfLCsg^)Y<_DtR~KKBa5Yc8^`PtH>%BJIg$MpRyzCP?-W7BQbV`9WSo zS(kwi%4%8`JU~=ebu+Ta__UK$JTclPBnY8~PjcMhT~#bcIcR(N9^mJMj!?sQxOb{1 z&QXrYD=pAUXrS3z#IA_Wx0dP8dKo%=9+cAWqP5l&x^HBPH`tuL%*nqpSlhgKK*hZH zUf_65B8Q@n&*91k&Q|OAbX{1>ZE7oB^TmA&e3n>7qxl^$svamtxfZ*BU9#@luWq{) z)g(JZt!r5Mtv{TY=cC1THk3aoS*nTC=?^Z+<6G9d_#q<)hxlzM*f_W(5j>W0#!{osP0X(v2 zmq&K9dB`hzysAsDe0@KqSEkKwUW&r={W(dke%l%s^q!eCTdG+lBVK2|?DJSyip}fw zz11*lwb({j<*W8qhmJ~VPWBqPM0q^fbTqsyHikb*sOhKrFBxa@52ubMPK+A1$Z0@pUH)+7Y?>HBQt(LLB&8>eo0 z<$f>Az#&5bu+@hl2XuMhf$G5O6^ca0N-3zp{Z{X4{KTi~QijY*rjcUsGKihU<0Gf2^_qI+K#BB`IuYv zshqNl;=b?xz48}U`TZv`uqRjmquzV>@j`vTTFxS0VV(c4e)0vRYn=KzHEFEZgc3fi z<8gZAe?;!Rm{}zvE~hNJw{~V%>jK6)+RL?oB^5tVgncP9-sEX=q=TwW?3#aQpCYmh>S?Mf50f&T`F3ure zP$41>i?B?1PC#{I*I7NyQxO-v+)}Gn)f)<;EVhe)U@DgbrFNag^kkmL6w^A5$&zUo zw@*&0-hjycdM)H~;Z-x|!GQJ60~Lheb&pO!Pzv2NEa}8pb@&-VT8M6l@9-@|*&$8pMD(js0IPk;{bo}%%yTc(w*5iaC z`wI1aCD~)$7@kI2#zLqTwOsZjhPOIG<5-Axh69)}8$~+OyqkOObq^XnP`{|_zJ!jw zeDcJH>#}`lyylR=LY=Q_WYaB__?Sz#XII{q`u*-H2@_4>Zi#^rGJ8o+NMbW$VsN2=6yrgi7$6n zMrBLXy%hbpG0I7jpp*h2mSN8Dn7feXqhr>akgDSNbsOZ?1g&{m( znUu3Von=Mj-=V@xF)Mk9eX6e5ktV|vFL85vRFr%b(4O?dw&@Rp(6*|41N0_wP>N;i zoQF&4FS)EULm6pTfkUJYtQsSi=Wn}%1bFpj5l?#=u;^7l3~3*zgygF{KVBM+zdC6j z%oviTJ{HBFQIl1?I$Eo#9d(dCUjCsmCKlDiwB6lr0WSvOyi2fs1IL|46(I#8A7HGQ9h%VxIZbjo*FihQdJ|lUMKIPXZ zK#C{TA7_f~p{UlC2c__gm3>qr%Ff-{Nyl^z^V;ZJ3D^yBYR^(NhF33(m9WI{|RIS!$2 z8_B2x^?Ey&RBM(l@^TPsfxc8D=(3p4ox0Z9#R&8~Gx=>?yUb}ClMQ{y?gv`shq0M8 zOKei|f`S|x<0PhPy7s_@klOE%nKR-0aw-1|TtAz4MRjP2WSn9iihSM-L!AFr zZ#9?_!*zD91PCJ8O{TSUH^|`*w|@eg-pZHJXndv09c$s*W>4E)*Vwa!m`tA2$1&@m z)RQ&SwnmhNU`mc2g$y$+GxB-0#Z1N^ck6YA(Tf)yQGYOT?o!nvZ#<619pPK#g2*ZB zQojjzD5;0(Dr|;9$k;-k`=$1^p~+BNrSGp&JzO{ zq)KUY+*|SB)I3FIvY2lZ$6YbM%I(f}rdWgS_?S)UjVZCps{3Vja87@iO~Hu9uboxC z;b_v_fdanjFz6kZ2b51DIQV=+rjWxIGQ=*XtTBS+$r~V+Qj2ms=hdO5%xpe7!R;F$ z2dDbi3$%I6qGS5E8=FGbZ~BsL#!@X2ZYtBJuE#8=MX$8rwKKm}>m~8&UUGpBnBNv{ z5ymglc+T)0RDOCJV@$SBAg^1ooFuHx08I@EQIP0POxxHMHtpU4&@j!=h*aoKf%T7U z1HyT=t3g?_w{*vwfwuC&Al>X<99GvhpGvgDXOxT~0x=zr!)qa)5$GMACKrUm{a@Yz z%}Qi+PAHtc=-v>}zn^IPhC=&JAmJp2D-HSX$tt4^MJ@R6C1gG#RQ*U|6;BeAZ3tt& z#ZM(>dB?FNqD#B=_iCp%1~}0)(4bAR&ZGmT_Rya#mq=3u1%+4lG57i`%@?DICSmw> zT<(U%S(Jl^v({u84flJ8YiPt7evV)1+Jxt`C^3B``3_iI&^thVu6 z^;7&+iM2JPLAA&69(_I2A+i*={8seYRc}wCAbS@(&^o^<7BEqBi1~%M$h4VHg7C!7 zkR+#hylLtLam4ZAxWU;)byp*>AivCIGCd-Iu z;7WqqXZy+LqO(0qvX>ciuzPiR>uYhaqvooTq>R7>s6gicM89%qmHb({GS*RWWJ#&S%q@Y|&jXvB&t&dbR(j zpD!*)a?W1fP8`>(^VXp=7RUd=q~g%N>L->)_(!X9wMfNVM6EAkm5Euu|HTTxmC7`V zG32V8y*1)}@%#7W^KHM?Mea6G#CN0iB2UJXg}UqpXZ$WS{P^GVME@wl{{QU1+In=t an~g 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 diff --git a/docs/benchmarks/dart/throughput.png b/docs/benchmarks/dart/throughput.png index f0c4d91d8aa9716d7612eaaa418cdbf34c07410f..d41f05638e5049e716c358bddfd18643829ee429 100644 GIT binary patch literal 87138 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 From fe79f9b83b225dff96a4c1947a9bf313b926daba Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Thu, 23 Apr 2026 18:56:22 +0800 Subject: [PATCH 14/14] docs(dart): fix benchmark markdown formatting --- docs/benchmarks/dart/README.md | 67 +++++++++++++++++----------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/docs/benchmarks/dart/README.md b/docs/benchmarks/dart/README.md index d210210b58..2348854a2d 100644 --- a/docs/benchmarks/dart/README.md +++ b/docs/benchmarks/dart/README.md @@ -4,47 +4,47 @@ This benchmark compares serialization and deserialization throughput for Apache ## Hardware and Runtime Info -| Key | Value | -| --- | --- | -| Timestamp | 2026-04-23T10:50:07.751368Z | -| OS | Version 15.7.2 (Build 24G325) | -| Host | MacBook-Pro.local | -| CPU Cores (Logical) | 12 | -| Memory (GB) | 48.00 | -| Dart | 3.10.7 (stable) (Tue Dec 23 00:01:57 2025 -0800) on "macos_arm64" | -| Samples per case | 5 | -| Warmup per case (s) | 1.0 | -| Duration per case (s) | 1.5 | +| Key | Value | +| --------------------- | ----------------------------------------------------------------- | +| Timestamp | 2026-04-23T10:50:07.751368Z | +| OS | Version 15.7.2 (Build 24G325) | +| Host | MacBook-Pro.local | +| CPU Cores (Logical) | 12 | +| Memory (GB) | 48.00 | +| Dart | 3.10.7 (stable) (Tue Dec 23 00:01:57 2025 -0800) on "macos_arm64" | +| Samples per case | 5 | +| Warmup per case (s) | 1.0 | +| Duration per case (s) | 1.5 | ## Throughput Results ![Throughput](throughput.png) -| Datatype | Operation | Fory TPS | Protobuf TPS | Fastest | -| --- | --- | ---: | ---: | --- | -| Struct | Serialize | 5,041,693 | 2,073,839 | fory (2.43x) | -| Struct | Deserialize | 6,395,290 | 4,991,881 | fory (1.28x) | -| Sample | Serialize | 1,783,688 | 552,140 | fory (3.23x) | -| Sample | Deserialize | 2,124,197 | 934,794 | fory (2.27x) | -| MediaContent | Serialize | 952,498 | 438,419 | fory (2.17x) | -| MediaContent | Deserialize | 1,649,039 | 737,340 | fory (2.24x) | -| StructList | Serialize | 1,945,119 | 399,007 | fory (4.87x) | -| StructList | Deserialize | 2,119,403 | 764,832 | fory (2.77x) | -| SampleList | Serialize | 475,413 | 52,512 | fory (9.05x) | -| SampleList | Deserialize | 508,939 | 116,236 | fory (4.38x) | -| MediaContentList | Serialize | 224,925 | 84,860 | fory (2.65x) | -| MediaContentList | Deserialize | 387,070 | 154,392 | fory (2.51x) | +| Datatype | Operation | Fory TPS | Protobuf TPS | Fastest | +| ---------------- | ----------- | --------: | -----------: | ------------ | +| Struct | Serialize | 5,041,693 | 2,073,839 | fory (2.43x) | +| Struct | Deserialize | 6,395,290 | 4,991,881 | fory (1.28x) | +| Sample | Serialize | 1,783,688 | 552,140 | fory (3.23x) | +| Sample | Deserialize | 2,124,197 | 934,794 | fory (2.27x) | +| MediaContent | Serialize | 952,498 | 438,419 | fory (2.17x) | +| MediaContent | Deserialize | 1,649,039 | 737,340 | fory (2.24x) | +| StructList | Serialize | 1,945,119 | 399,007 | fory (4.87x) | +| StructList | Deserialize | 2,119,403 | 764,832 | fory (2.77x) | +| SampleList | Serialize | 475,413 | 52,512 | fory (9.05x) | +| SampleList | Deserialize | 508,939 | 116,236 | fory (4.38x) | +| MediaContentList | Serialize | 224,925 | 84,860 | fory (2.65x) | +| MediaContentList | Deserialize | 387,070 | 154,392 | fory (2.51x) | ## Serialized Size (bytes) -| Datatype | Fory | Protobuf | -| --- | ---: | ---: | -| Struct | 58 | 61 | -| Sample | 446 | 377 | -| MediaContent | 365 | 307 | -| StructList | 184 | 315 | -| SampleList | 1980 | 1900 | -| MediaContentList | 1535 | 1550 | +| Datatype | Fory | Protobuf | +| ---------------- | ---: | -------: | +| Struct | 58 | 61 | +| Sample | 446 | 377 | +| MediaContent | 365 | 307 | +| StructList | 184 | 315 | +| SampleList | 1980 | 1900 | +| MediaContentList | 1535 | 1550 | ## Per-workload Plots @@ -71,4 +71,3 @@ This benchmark compares serialization and deserialization throughput for Apache ### MediaContentList ![MediaContentList](mediacontentlist.png) -