diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index c15237859..f33886658 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -45,8 +45,8 @@ jobs: id: update-libraryVersion uses: jacobtomlinson/gha-find-replace@b76729678e8d52dadb12e0e16454a93e301a919d #! 2.0.0 with: - find: "static const libraryVersion = '[^']*';" - replace: "static const libraryVersion = '${{ steps.update-changelog.outputs.new-version }}';" + find: "const libraryVersion = '[^']*';" + replace: "const libraryVersion = '${{ steps.update-changelog.outputs.new-version }}';" include: 'lib/src/native/realm_core.dart' - name: Make sure we updated libraryVersion in realm_core.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index a12064eb6..5148e2194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Added `SyncWebSocketError` and `SyncWebSocketErrorCode` for web socket connection sync errors ([#1182](https://github.com/realm/realm-dart/pull/1182)). * Added `FlexibleSyncConfiguration.shouldCompactCallback` support ([#1204](https://github.com/realm/realm-dart/pull/1204)). * Added `RealmSet.asResults()` ([#1214](https://github.com/realm/realm-dart/pull/1214)). +* Support `Decimal128` datatype ([#1192](https://github.com/realm/realm-dart/pull/1192)). ### Fixed * You may have a crash on Windows if you try to open a file with non-ASCII path (Core upgrade). diff --git a/common/lib/src/realm_types.dart b/common/lib/src/realm_types.dart index 3ffce22df..0ae19d1bf 100644 --- a/common/lib/src/realm_types.dart +++ b/common/lib/src/realm_types.dart @@ -101,7 +101,7 @@ class RealmStateError extends StateError implements RealmError { } /// @nodoc -class Decimal128 {} // TODO Support decimal128 datatype https://github.com/realm/realm-dart/issues/725 +abstract class Decimal128 {} /// @nodoc abstract class RealmObjectBaseMarker {} @@ -158,7 +158,7 @@ class RealmValue { const RealmValue.realmObject(RealmObjectMarker o) : this._(o); const RealmValue.dateTime(DateTime timestamp) : this._(timestamp); const RealmValue.objectId(ObjectId id) : this._(id); - // const RealmValue.decimal128(Decimal128 decimal) : this._(decimal); // not supported yet + const RealmValue.decimal128(Decimal128 decimal) : this._(decimal); const RealmValue.uuid(Uuid uuid) : this._(uuid); /// Will throw [ArgumentError] @@ -172,7 +172,7 @@ class RealmValue { o is RealmObjectMarker || o is DateTime || o is ObjectId || - // o is Decimal128 || // not supported yet + o is Decimal128 || o is Uuid) { return RealmValue._(o); } else { diff --git a/ffigen/config.yaml b/ffigen/config.yaml index 591384b1b..effbe66ca 100644 --- a/ffigen/config.yaml +++ b/ffigen/config.yaml @@ -6,11 +6,13 @@ headers: entry-points: - 'realm.h' - 'realm_dart.h' + - 'realm_dart_decimal128.h' - 'realm_dart_scheduler.h' - 'realm_dart_sync.h' - include-directives: #generate only for these headers + include-directives: # generate only for these headers - 'realm.h' - 'realm_dart.h' + - 'realm_dart_decimal128.h' - 'realm_dart_scheduler.h' - 'realm_dart_sync.h' compiler-opts: @@ -21,8 +23,8 @@ compiler-opts: sort: true functions: exclude: - - '_.*' #exclude all starting with _ - - 'Dart_.*' #exlude all starting with Dart_ + - '_.*' # exclude all starting with _ + - 'Dart_.*' # exclude all starting with Dart_ symbol-address: include: - 'realm_dart_.*' @@ -30,11 +32,11 @@ functions: structs: exclude: - '_.*' - - 'Dart_.*' #exlude all starting with Dart_ + - 'Dart_.*' # exclude all starting with Dart_ globals: exclude: - '_.*' - - 'RLM_.*' #exlude RLM_ globals constants since they are not exported from binary + - 'RLM_.*' # exclude RLM_ globals constants since they are not exported from binary unions: exclude: - '_.*' diff --git a/ffigen/realm_dart_decimal128.h b/ffigen/realm_dart_decimal128.h new file mode 120000 index 000000000..dad18748a --- /dev/null +++ b/ffigen/realm_dart_decimal128.h @@ -0,0 +1 @@ +../src/realm_dart_decimal128.h \ No newline at end of file diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index 87fbc55d3..8b1083766 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -11,6 +11,7 @@ import '../test/backlinks_test.dart' as backlinks_test; import '../test/client_reset_test.dart' as client_reset_test; import '../test/configuration_test.dart' as configuration_test; import '../test/credentials_test.dart' as credentials_test; +import '../test/decimal128_test.dart' as decimal128_test; import '../test/dynamic_realm_test.dart' as dynamic_realm_test; import '../test/embedded_test.dart' as embedded_test; import '../test/indexed_test.dart' as indexed_test; @@ -36,6 +37,7 @@ Future main(List args) async { await client_reset_test.main(args); await configuration_test.main(args); await credentials_test.main(args); + await decimal128_test.main(args); await dynamic_realm_test.main(args); await embedded_test.main(args); indexed_test.main(args); diff --git a/generator/lib/src/dart_type_ex.dart b/generator/lib/src/dart_type_ex.dart index 8c1c6d35f..bb45bead4 100644 --- a/generator/lib/src/dart_type_ex.dart +++ b/generator/lib/src/dart_type_ex.dart @@ -15,7 +15,7 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// -import 'dart:ffi'; + import 'dart:typed_data'; import 'package:analyzer/dart/element/nullability_suffix.dart'; @@ -29,6 +29,7 @@ import 'type_checkers.dart'; extension DartTypeEx on DartType { bool isExactly() => TypeChecker.fromRuntime(T).isExactlyType(this); + bool isA() => TypeChecker.fromRuntime(T).isAssignableFromType(this); bool get isRealmValue => const TypeChecker.fromRuntime(RealmValue).isAssignableFromType(this); bool get isRealmCollection => realmCollectionType != RealmCollectionType.none; @@ -112,7 +113,7 @@ extension DartTypeEx on DartType { if (isRealmValue) return RealmPropertyType.mixed; if (isExactly()) return RealmPropertyType.timestamp; if (isDartCoreNum || isDartCoreDouble) return RealmPropertyType.double; - if (isExactly()) return RealmPropertyType.decimal128; + if (isA()) return RealmPropertyType.decimal128; if (isRealmModel) return RealmPropertyType.object; if (isDartCoreIterable) return RealmPropertyType.linkingObjects; if (isExactly()) return RealmPropertyType.objectid; diff --git a/generator/lib/src/field_element_ex.dart b/generator/lib/src/field_element_ex.dart index 59ccb2642..bbdf676ef 100644 --- a/generator/lib/src/field_element_ex.dart +++ b/generator/lib/src/field_element_ex.dart @@ -92,7 +92,7 @@ extension FieldElementEx on FieldElement { final backlink = backlinkInfo; // Check for as-of-yet unsupported type - if (type.isDartCoreMap || type.isExactly()) { + if (type.isDartCoreMap) { throw RealmInvalidGenerationSourceError( 'Field type not supported yet', element: this, diff --git a/generator/test/good_test_data/all_types.dart b/generator/test/good_test_data/all_types.dart index 4f86173a8..fde9f6042 100644 --- a/generator/test/good_test_data/all_types.dart +++ b/generator/test/good_test_data/all_types.dart @@ -41,6 +41,8 @@ class _Bar { late RealmValue any; late List manyAny; + + late Decimal128 decimal; } @RealmModel() @@ -50,4 +52,4 @@ class _PrimitiveTypes { late DateTime dateProp; late double doubleProp; late ObjectId objectIdProp; -} \ No newline at end of file +} diff --git a/generator/test/good_test_data/all_types.expected b/generator/test/good_test_data/all_types.expected index 2ba7baa9a..f302dbf41 100644 --- a/generator/test/good_test_data/all_types.expected +++ b/generator/test/good_test_data/all_types.expected @@ -57,7 +57,8 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject { bool aBool, bool another, ObjectId objectId, - Uuid uuid, { + Uuid uuid, + Decimal128 decimal, { Uint8List data = Uint8List(16), DateTime timestamp = DateTime.now(), double aDouble = 0.0, @@ -85,6 +86,7 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject { RealmObjectBase.set(this, 'uuid', uuid); RealmObjectBase.set(this, 'anOptionalString', anOptionalString); RealmObjectBase.set(this, 'any', any); + RealmObjectBase.set(this, 'decimal', decimal); RealmObjectBase.set>(this, 'list', RealmList(list)); RealmObjectBase.set>( this, 'manyAny', RealmList(manyAny)); @@ -167,6 +169,12 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject { set manyAny(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + Decimal128 get decimal => + RealmObjectBase.get(this, 'decimal') as Decimal128; + @override + set decimal(Decimal128 value) => RealmObjectBase.set(this, 'decimal', value); + @override RealmResults get foos => RealmObjectBase.get(this, 'foos') as RealmResults; @@ -204,6 +212,7 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject { SchemaProperty('any', RealmPropertyType.mixed, optional: true), SchemaProperty('manyAny', RealmPropertyType.mixed, optional: true, collectionType: RealmCollectionType.list), + SchemaProperty('decimal', RealmPropertyType.decimal128), SchemaProperty('foos', RealmPropertyType.linkingObjects, linkOriginProperty: 'bar', collectionType: RealmCollectionType.list, diff --git a/lib/src/native/decimal128.dart b/lib/src/native/decimal128.dart new file mode 100644 index 000000000..ff2432e2e --- /dev/null +++ b/lib/src/native/decimal128.dart @@ -0,0 +1,149 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// Licensed 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. +// +//////////////////////////////////////////////////////////////////////////////// + +part of 'realm_core.dart'; + +/// A 128-bit decimal floating point number. +class Decimal128 extends Comparable implements common.Decimal128 { + /// The value 0. + static final zero = Decimal128.fromInt(0); + + /// The value 1. + static final one = Decimal128.fromInt(1); + + /// The value 10. + static final ten = Decimal128.fromInt(10); + + /// The value NaN. + static final nan = Decimal128._(_realmLib.realm_dart_decimal128_nan()); + + /// The value +Inf. + static final infinity = one / zero; // +Inf + + /// The value -Inf. + static final negativeInfinity = -infinity; + + final realm_decimal128_t _value; + + Decimal128._(this._value); + + static final _validInput = RegExp(r'^[+-]?((\d+\.?\d*|\d*\.?\d+)([eE][+-]?\d+)?|NaN|Inf(inity)?)$'); + + /// Parses a string into a [Decimal128]. Returns `null` if the string is not a valid [Decimal128]. + static Decimal128? tryParse(String source) { + if (!_validInput.hasMatch(source)) return null; + return using((arena) { + final result = _realmLib.realm_dart_decimal128_from_string(source.toCharPtr(arena)); + return Decimal128._(result); + }); + } + + /// Parses a string into a [Decimal128]. Throws a [FormatException] if the string is not a valid [Decimal128]. + factory Decimal128.parse(String source) { + return tryParse(source) ?? (throw FormatException('Invalid Decimal128', source)); + } + + /// Converts a `int` into a [Decimal128]. + factory Decimal128.fromInt(int value) { + return Decimal128._(_realmLib.realm_dart_decimal128_from_int64(value)); + } + + /// Converts a `double` into a [Decimal128]. + factory Decimal128.fromDouble(double value) { + return Decimal128.parse(value.toString()); // TODO(kn): Find a way to optimize this + } + + /// Returns `true` if `this` is NaN. + bool get isNaN => _realmLib.realm_dart_decimal128_is_nan(_value); + + /// Adds `this` with `other` and returns a new [Decimal128]. + Decimal128 operator +(Decimal128 other) { + return Decimal128._(_realmLib.realm_dart_decimal128_add(_value, other._value)); + } + + /// Subtracts `other` from `this` and returns a new [Decimal128]. + Decimal128 operator -(Decimal128 other) { + return Decimal128._(_realmLib.realm_dart_decimal128_subtract(_value, other._value)); + } + + /// Multiplies `this` with `other` and returns a new [Decimal128]. + Decimal128 operator *(Decimal128 other) { + return Decimal128._(_realmLib.realm_dart_decimal128_multiply(_value, other._value)); + } + + /// Divides `this` by `other` and returns a new [Decimal128]. + Decimal128 operator /(Decimal128 other) { + return Decimal128._(_realmLib.realm_dart_decimal128_divide(_value, other._value)); + } + + /// Negates `this` and returns a new [Decimal128]. + Decimal128 operator -() => Decimal128._(_realmLib.realm_dart_decimal128_negate(_value)); + + /// Returns the absolute value of `this`. + Decimal128 abs() => this < zero ? -this : this; + + /// Returns `true` if `this` and `other` are equal. + @override + // ignore: hash_and_equals + operator ==(Object other) { + // WARNING: Don't use identical to ensure nan != nan, + // if (identical(this, other)) return true; + if (other is Decimal128) { + return _realmLib.realm_dart_decimal128_equal(_value, other._value); + } + return false; + } + + /// Returns `true` if `this` is less than `other`. + bool operator <(Decimal128 other) { + return _realmLib.realm_dart_decimal128_less_than(_value, other._value); + } + + /// Returns `true` if `this` is less than or equal to `other`. + bool operator <=(Decimal128 other) => compareTo(other) <= 0; + + /// Returns `true` if `this` is greater than `other`. + bool operator >(Decimal128 other) { + return _realmLib.realm_dart_decimal128_greater_than(_value, other._value); + } + + /// Returns `true` if `this` is greater than or equal to `other`. + bool operator >=(Decimal128 other) => compareTo(other) >= 0; + + /// Converts `this` to an `int`. Possibly loosing precision. + int toInt() => _realmLib.realm_dart_decimal128_to_int64(_value); + + /// String representation of `this`. + @override + String toString() { + return using((arena) { + final realmString = _realmLib.realm_dart_decimal128_to_string(_value); + return ascii.decode(realmString.data.cast().asTypedList(realmString.size)); + }); + } + + /// Compares `this` to `other`. + @override + int compareTo(Decimal128 other) => _realmLib.realm_dart_decimal128_compare_to(_value, other._value); +} + +extension Decimal128Internal on Decimal128 { + realm_decimal128_t get value => _value; + + static Decimal128 fromNative(realm_decimal128_t value) => Decimal128._(value); +} diff --git a/lib/src/native/realm_bindings.dart b/lib/src/native/realm_bindings.dart index e9f2f458b..9f1e11bd9 100644 --- a/lib/src/native/realm_bindings.dart +++ b/lib/src/native/realm_bindings.dart @@ -3182,6 +3182,266 @@ class RealmLibrary { late final _realm_dart_create_scheduler = _realm_dart_create_schedulerPtr .asFunction Function(int, int)>(); + realm_decimal128_t realm_dart_decimal128_add( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_add( + x, + y, + ); + } + + late final _realm_dart_decimal128_addPtr = _lookup< + ffi.NativeFunction< + realm_decimal128_t Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_add'); + late final _realm_dart_decimal128_add = + _realm_dart_decimal128_addPtr.asFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>(); + + int realm_dart_decimal128_compare_to( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_compare_to( + x, + y, + ); + } + + late final _realm_dart_decimal128_compare_toPtr = _lookup< + ffi.NativeFunction< + ffi.Int Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_compare_to'); + late final _realm_dart_decimal128_compare_to = + _realm_dart_decimal128_compare_toPtr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_copy( + realm_decimal128_t x, + ) { + return _realm_dart_decimal128_copy( + x, + ); + } + + late final _realm_dart_decimal128_copyPtr = _lookup< + ffi.NativeFunction>( + 'realm_dart_decimal128_copy'); + late final _realm_dart_decimal128_copy = _realm_dart_decimal128_copyPtr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_divide( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_divide( + x, + y, + ); + } + + late final _realm_dart_decimal128_dividePtr = _lookup< + ffi.NativeFunction< + realm_decimal128_t Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_divide'); + late final _realm_dart_decimal128_divide = + _realm_dart_decimal128_dividePtr.asFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>(); + + bool realm_dart_decimal128_equal( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_equal( + x, + y, + ); + } + + late final _realm_dart_decimal128_equalPtr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_equal'); + late final _realm_dart_decimal128_equal = _realm_dart_decimal128_equalPtr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_from_int64( + int low, + ) { + return _realm_dart_decimal128_from_int64( + low, + ); + } + + late final _realm_dart_decimal128_from_int64Ptr = + _lookup>( + 'realm_dart_decimal128_from_int64'); + late final _realm_dart_decimal128_from_int64 = + _realm_dart_decimal128_from_int64Ptr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_from_string( + ffi.Pointer string, + ) { + return _realm_dart_decimal128_from_string( + string, + ); + } + + late final _realm_dart_decimal128_from_stringPtr = _lookup< + ffi.NativeFunction< + realm_decimal128_t Function( + ffi.Pointer)>>('realm_dart_decimal128_from_string'); + late final _realm_dart_decimal128_from_string = + _realm_dart_decimal128_from_stringPtr + .asFunction)>(); + + bool realm_dart_decimal128_greater_than( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_greater_than( + x, + y, + ); + } + + late final _realm_dart_decimal128_greater_thanPtr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_greater_than'); + late final _realm_dart_decimal128_greater_than = + _realm_dart_decimal128_greater_thanPtr + .asFunction(); + + bool realm_dart_decimal128_is_nan( + realm_decimal128_t x, + ) { + return _realm_dart_decimal128_is_nan( + x, + ); + } + + late final _realm_dart_decimal128_is_nanPtr = + _lookup>( + 'realm_dart_decimal128_is_nan'); + late final _realm_dart_decimal128_is_nan = _realm_dart_decimal128_is_nanPtr + .asFunction(); + + bool realm_dart_decimal128_less_than( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_less_than( + x, + y, + ); + } + + late final _realm_dart_decimal128_less_thanPtr = _lookup< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_less_than'); + late final _realm_dart_decimal128_less_than = + _realm_dart_decimal128_less_thanPtr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_multiply( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_multiply( + x, + y, + ); + } + + late final _realm_dart_decimal128_multiplyPtr = _lookup< + ffi.NativeFunction< + realm_decimal128_t Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_multiply'); + late final _realm_dart_decimal128_multiply = + _realm_dart_decimal128_multiplyPtr.asFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>(); + + realm_decimal128_t realm_dart_decimal128_nan() { + return _realm_dart_decimal128_nan(); + } + + late final _realm_dart_decimal128_nanPtr = + _lookup>( + 'realm_dart_decimal128_nan'); + late final _realm_dart_decimal128_nan = + _realm_dart_decimal128_nanPtr.asFunction(); + + realm_decimal128_t realm_dart_decimal128_negate( + realm_decimal128_t x, + ) { + return _realm_dart_decimal128_negate( + x, + ); + } + + late final _realm_dart_decimal128_negatePtr = _lookup< + ffi.NativeFunction>( + 'realm_dart_decimal128_negate'); + late final _realm_dart_decimal128_negate = _realm_dart_decimal128_negatePtr + .asFunction(); + + realm_decimal128_t realm_dart_decimal128_subtract( + realm_decimal128_t x, + realm_decimal128_t y, + ) { + return _realm_dart_decimal128_subtract( + x, + y, + ); + } + + late final _realm_dart_decimal128_subtractPtr = _lookup< + ffi.NativeFunction< + realm_decimal128_t Function(realm_decimal128_t, + realm_decimal128_t)>>('realm_dart_decimal128_subtract'); + late final _realm_dart_decimal128_subtract = + _realm_dart_decimal128_subtractPtr.asFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>(); + + int realm_dart_decimal128_to_int64( + realm_decimal128_t x, + ) { + return _realm_dart_decimal128_to_int64( + x, + ); + } + + late final _realm_dart_decimal128_to_int64Ptr = + _lookup>( + 'realm_dart_decimal128_to_int64'); + late final _realm_dart_decimal128_to_int64 = + _realm_dart_decimal128_to_int64Ptr + .asFunction(); + + realm_string_t realm_dart_decimal128_to_string( + realm_decimal128_t x, + ) { + return _realm_dart_decimal128_to_string( + x, + ); + } + + late final _realm_dart_decimal128_to_stringPtr = + _lookup>( + 'realm_dart_decimal128_to_string'); + late final _realm_dart_decimal128_to_string = + _realm_dart_decimal128_to_stringPtr + .asFunction(); + void realm_dart_delete_persistent_handle( ffi.Pointer handle, ) { @@ -3617,21 +3877,21 @@ class RealmLibrary { late final _realm_delete_files = _realm_delete_filesPtr.asFunction< bool Function(ffi.Pointer, ffi.Pointer)>(); - void realm_dettach_finalizer( + void realm_detach_finalizer( ffi.Pointer finalizableHandle, Object handle, ) { - return _realm_dettach_finalizer( + return _realm_detach_finalizer( finalizableHandle, handle, ); } - late final _realm_dettach_finalizerPtr = _lookup< + late final _realm_detach_finalizerPtr = _lookup< ffi.NativeFunction< ffi.Void Function( - ffi.Pointer, ffi.Handle)>>('realm_dettach_finalizer'); - late final _realm_dettach_finalizer = _realm_dettach_finalizerPtr + ffi.Pointer, ffi.Handle)>>('realm_detach_finalizer'); + late final _realm_detach_finalizer = _realm_detach_finalizerPtr .asFunction, Object)>(); /// Subscribe to notifications for this object. @@ -10466,6 +10726,75 @@ class _SymbolAddresses { ffi.Pointer Function(ffi.Uint64, Dart_Port)>> get realm_dart_create_scheduler => _library._realm_dart_create_schedulerPtr; + ffi.Pointer< + ffi.NativeFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_add => _library._realm_dart_decimal128_addPtr; + ffi.Pointer< + ffi.NativeFunction< + ffi.Int Function(realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_compare_to => + _library._realm_dart_decimal128_compare_toPtr; + ffi.Pointer< + ffi.NativeFunction> + get realm_dart_decimal128_copy => _library._realm_dart_decimal128_copyPtr; + ffi.Pointer< + ffi.NativeFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_divide => + _library._realm_dart_decimal128_dividePtr; + ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_equal => + _library._realm_dart_decimal128_equalPtr; + ffi.Pointer> + get realm_dart_decimal128_from_int64 => + _library._realm_dart_decimal128_from_int64Ptr; + ffi.Pointer< + ffi.NativeFunction< + realm_decimal128_t Function(ffi.Pointer)>> + get realm_dart_decimal128_from_string => + _library._realm_dart_decimal128_from_stringPtr; + ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_greater_than => + _library._realm_dart_decimal128_greater_thanPtr; + ffi.Pointer> + get realm_dart_decimal128_is_nan => + _library._realm_dart_decimal128_is_nanPtr; + ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_less_than => + _library._realm_dart_decimal128_less_thanPtr; + ffi.Pointer< + ffi.NativeFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_multiply => + _library._realm_dart_decimal128_multiplyPtr; + ffi.Pointer> + get realm_dart_decimal128_nan => _library._realm_dart_decimal128_nanPtr; + ffi.Pointer< + ffi.NativeFunction> + get realm_dart_decimal128_negate => + _library._realm_dart_decimal128_negatePtr; + ffi.Pointer< + ffi.NativeFunction< + realm_decimal128_t Function( + realm_decimal128_t, realm_decimal128_t)>> + get realm_dart_decimal128_subtract => + _library._realm_dart_decimal128_subtractPtr; + ffi.Pointer> + get realm_dart_decimal128_to_int64 => + _library._realm_dart_decimal128_to_int64Ptr; + ffi.Pointer> + get realm_dart_decimal128_to_string => + _library._realm_dart_decimal128_to_stringPtr; ffi.Pointer)>> get realm_dart_delete_persistent_handle => _library._realm_dart_delete_persistent_handlePtr; diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index b34ce1e87..1a778ad0f 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -28,7 +28,8 @@ import 'package:cancellation_token/cancellation_token.dart'; import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; -import 'package:realm_common/realm_common.dart'; +import 'package:realm_common/realm_common.dart' hide Decimal128; +import 'package:realm_common/realm_common.dart' as common show Decimal128; import '../app.dart'; import '../collections.dart'; @@ -47,9 +48,24 @@ import '../user.dart'; import '../set.dart'; import 'realm_bindings.dart'; -late RealmLibrary _realmLib; +part 'decimal128.dart'; -final _RealmCore realmCore = _RealmCore(); +const bugInTheSdkMessage = "This is likely a bug in the Realm SDK - please file an issue at https://github.com/realm/realm-dart/issues"; + +final _realmLib = () { + final result = RealmLibrary(initRealm()); + final nativeLibraryVersion = result.realm_dart_library_version().cast().toDartString(); + if (libraryVersion != nativeLibraryVersion) { + final additionMessage = + isFlutterPlatform ? bugInTheSdkMessage : "Did you forget to run `dart run realm_dart install` after upgrading the realm_dart package?"; + throw RealmException('Realm SDK package version does not match the native library version ($libraryVersion != $nativeLibraryVersion). $additionMessage'); + } + return result; +}(); + +// stamped into the library by the build system (see prepare-release.yml) +const libraryVersion = '1.0.3'; +final realmCore = _RealmCore(); class _RealmCore { // From realm.h. Currently not exported from the shared library @@ -59,33 +75,7 @@ class _RealmCore { // ignore: unused_field static const int RLM_INVALID_OBJECT_KEY = -1; - final int encryptionKeySize = 64; - - static Object noopUserdata = Object(); - - final bugInTheSdkMessage = "This is likely a bug in the Realm SDK - please file an issue at https://github.com/realm/realm-dart/issues"; - - // Hide the RealmCore class and make it a singleton - static _RealmCore? _instance; - late final int isolateKey; - - _RealmCore._() { - final lib = initRealm(); - _realmLib = RealmLibrary(lib); - if (libraryVersion != nativeLibraryVersion) { - final additionMessage = - isFlutterPlatform ? bugInTheSdkMessage : "Did you forget to run `dart run realm_dart install` after upgrading the realm_dart package?"; - throw RealmException('Realm SDK package version does not match the native library version ($libraryVersion != $nativeLibraryVersion). $additionMessage'); - } - } - - factory _RealmCore() { - return _instance ??= _RealmCore._(); - } - - // stamped into the library by the build system (see prepare-release.yml) - static const libraryVersion = '1.0.3'; - late String nativeLibraryVersion = _realmLib.realm_dart_library_version().cast().toDartString(); + final encryptionKeySize = 64; // for debugging only. Enable in realm_dart.cpp // void invokeGC() { @@ -1695,7 +1685,7 @@ class _RealmCore { _realmLib.realm_sync_client_config_set_log_level(handle._pointer, Realm.logger.level.toInt()); final logCallback = Pointer.fromFunction)>(_logCallback); - final logCallbackUserdata = _realmLib.realm_dart_userdata_async_new(noopUserdata, logCallback.cast(), scheduler.handle._pointer); + final logCallbackUserdata = _realmLib.realm_dart_userdata_async_new(const Object(), logCallback.cast(), scheduler.handle._pointer); _realmLib.realm_sync_client_config_set_log_callback(handle._pointer, _realmLib.addresses.realm_dart_sync_client_log_callback, logCallbackUserdata.cast(), _realmLib.addresses.realm_dart_userdata_async_free); @@ -2568,7 +2558,7 @@ abstract class HandleBase implements Finalizable { _releaseCore(); if (!isUnowned) { - _realmLib.realm_dettach_finalizer(_finalizableHandle, this); + _realmLib.realm_detach_finalizer(_finalizableHandle, this); _realmLib.realm_release(_pointer.cast()); } @@ -2872,6 +2862,9 @@ void _intoRealmValue(Object? value, Pointer realm_value, Allocato realm_value.ref.type = realm_value_type.RLM_TYPE_TIMESTAMP; } else if (value is RealmValue) { return _intoRealmValue(value.value, realm_value, allocator); + } else if (value is Decimal128) { + realm_value.ref.values.decimal128 = value.value; + realm_value.ref.type = realm_value_type.RLM_TYPE_DECIMAL128; } else { throw RealmException("Property type ${value.runtimeType} not supported"); } @@ -2909,7 +2902,9 @@ extension on Pointer { final nanoseconds = ref.values.timestamp.nanoseconds; return DateTime.fromMicrosecondsSinceEpoch(seconds * _microsecondsPerSecond + nanoseconds ~/ _nanosecondsPerMicrosecond, isUtc: true); case realm_value_type.RLM_TYPE_DECIMAL128: - throw Exception("Not implemented"); + var decimal = ref.values.decimal128; // NOTE: Does not copy the struct! + decimal = _realmLib.realm_dart_decimal128_copy(decimal); // This is a workaround to that + return Decimal128Internal.fromNative(decimal); case realm_value_type.RLM_TYPE_OBJECT_ID: return ObjectId.fromBytes(cast().asTypedList(12)); case realm_value_type.RLM_TYPE_UUID: diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index f22287875..0556e0f84 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -121,6 +121,7 @@ export 'session.dart' SyncSessionErrorCode; export 'subscription.dart' show Subscription, SubscriptionSet, SubscriptionSetState, MutableSubscriptionSet; export 'user.dart' show User, UserState, ApiKeyClient, UserIdentity, ApiKey, FunctionsClient; +export 'native/realm_core.dart' show Decimal128; /// A [Realm] instance represents a `Realm` database. /// diff --git a/lib/src/realm_object.dart b/lib/src/realm_object.dart index 08fc8c94c..e983f2cf8 100644 --- a/lib/src/realm_object.dart +++ b/lib/src/realm_object.dart @@ -425,7 +425,7 @@ mixin RealmObjectBase on RealmEntity implements RealmObjectBaseMarker, Finalizab if (invocation.isGetter) { final name = _symbolRegex.firstMatch(invocation.memberName.toString())?.namedGroup("symbolName"); if (name == null) { - throw RealmError("Could not find symbol name for ${invocation.memberName}. ${realmCore.bugInTheSdkMessage}"); + throw RealmError("Could not find symbol name for ${invocation.memberName}. $bugInTheSdkMessage"); } return get(this, name); @@ -434,7 +434,7 @@ mixin RealmObjectBase on RealmEntity implements RealmObjectBaseMarker, Finalizab if (invocation.isSetter) { final name = _symbolRegex.firstMatch(invocation.memberName.toString())?.namedGroup("symbolName"); if (name == null) { - throw RealmError("Could not find symbol name for ${invocation.memberName}. ${realmCore.bugInTheSdkMessage}"); + throw RealmError("Could not find symbol name for ${invocation.memberName}. $bugInTheSdkMessage"); } return set(this, name, invocation.positionalArguments.single); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 62a00aad0..4c7cdf064 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,14 +9,17 @@ add_subdirectory(dart-dl) set(SOURCES realm_dart.cpp + realm_dart_decimal128.cpp realm_dart_scheduler.cpp realm_dart_sync.cpp + realm-core/src/external/IntelRDFPMathLib20U2/LIBRARY/src/bid128_noncomp.c ) set(HEADERS realm_dart.h realm_dart.hpp realm_dart_scheduler.h + realm_dart_scheduler.h realm_dart_sync.h realm-core/src/realm.h ) diff --git a/src/realm_dart.cpp b/src/realm_dart.cpp index 1fadb181b..fe85af72d 100644 --- a/src/realm_dart.cpp +++ b/src/realm_dart.cpp @@ -55,7 +55,7 @@ RLM_API void realm_dart_initializeDartApiDL(void* data) { class WeakHandle { public: - WeakHandle(Dart_Handle handle) : m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) { + WeakHandle(Dart_Handle handle): m_weakHandle(Dart_NewWeakPersistentHandle_DL(handle, this, 1, finalize_handle)) { } Dart_Handle value() { @@ -129,19 +129,19 @@ RLM_API const char* realm_dart_library_version() { return "1.0.3"; } // } void handle_finalizer(void* isolate_callback_data, void* realmPtr) { - realm_release(realmPtr); + realm_release(realmPtr); } RLM_API void* realm_attach_finalizer(Dart_Handle handle, void* realmPtr, int size) { - return Dart_NewFinalizableHandle_DL(handle, realmPtr, size, handle_finalizer); + return Dart_NewFinalizableHandle_DL(handle, realmPtr, size, handle_finalizer); } -RLM_API void realm_dettach_finalizer(void* finalizableHandle, Dart_Handle handle) { - Dart_FinalizableHandle finalHandle = reinterpret_cast(finalizableHandle); - return Dart_DeleteFinalizableHandle_DL(finalHandle, handle); +RLM_API void realm_detach_finalizer(void* finalizableHandle, Dart_Handle handle) { + Dart_FinalizableHandle finalHandle = reinterpret_cast(finalizableHandle); + return Dart_DeleteFinalizableHandle_DL(finalHandle, handle); } -RLM_API void realm_set_auto_refresh(realm_t* realm, bool enable){ +RLM_API void realm_set_auto_refresh(realm_t* realm, bool enable) { (*realm)->set_auto_refresh(enable); } diff --git a/src/realm_dart.h b/src/realm_dart.h index a1c460f1b..3e4f8d844 100644 --- a/src/realm_dart.h +++ b/src/realm_dart.h @@ -51,6 +51,7 @@ RLM_API const char* realm_dart_library_version(); // RLM_API void realm_dart_gc(); RLM_API void* realm_attach_finalizer(Dart_Handle handle, void* realmPtr, int size); -RLM_API void realm_dettach_finalizer(void* finalizableHandle, Dart_Handle handle); +RLM_API void realm_detach_finalizer(void* finalizableHandle, Dart_Handle handle); RLM_API void realm_set_auto_refresh(realm_t* realm, bool enable); + #endif // REALM_DART_H \ No newline at end of file diff --git a/src/realm_dart_decimal128.cpp b/src/realm_dart_decimal128.cpp new file mode 100644 index 000000000..97695405d --- /dev/null +++ b/src/realm_dart_decimal128.cpp @@ -0,0 +1,149 @@ +#include "realm_dart.hpp" +#include "realm-core/src/external/IntelRDFPMathLib20U2/LIBRARY/src/bid_conf.h" +#include "realm-core/src/external/IntelRDFPMathLib20U2/LIBRARY/src/bid_functions.h" + +namespace { +realm_decimal128_t to_decimal128(const BID_UINT128& value) +{ + realm_decimal128_t result; + memcpy(&result, &value, sizeof(BID_UINT128)); + return result; +} + +BID_UINT128 to_BID_UINT128(const realm_decimal128_t& value) +{ + BID_UINT128 result; + memcpy(&result, &value, sizeof(BID_UINT128)); + return result; +} +} + +RLM_API realm_decimal128_t realm_dart_decimal128_from_string(const char* string) { + unsigned int flags = 0; + BID_UINT128 result; + bid128_from_string(&result, const_cast(string), &flags); + return to_decimal128(result); +} + +RLM_API realm_string_t realm_dart_decimal128_to_string(realm_decimal128_t x) { + auto x_bid = to_BID_UINT128(x); + // This buffer is reused between calls, hence the static keyword + static char buffer[34]; // 34 bytes is the maximum length of a string representation of a decimal128 + unsigned int flags = 0; + bid128_to_string(buffer, &x_bid, &flags); + return realm_string_t{ buffer, strlen(buffer) }; +} + +RLM_API realm_decimal128_t realm_dart_decimal128_nan() { + BID_UINT128 result; + bid128_nan(&result, "+NaN"); + return to_decimal128(result); +} + +RLM_API bool realm_dart_decimal128_is_nan(realm_decimal128_t x) { + auto x_bid = to_BID_UINT128(x); + int result; + bid128_isNaN(&result, &x_bid); + return result; +} + +RLM_API realm_decimal128_t realm_dart_decimal128_from_int64(int64_t x) { + BID_UINT128 result; + BID_SINT64 y = x; + bid128_from_int64(&result, &y); + return to_decimal128(result); +} + +RLM_API int64_t realm_dart_decimal128_to_int64(realm_decimal128_t x) { + auto x_bid = to_BID_UINT128(x); + BID_SINT64 result; + unsigned int flags = 0; + bid128_to_int64_int(&result, &x_bid, &flags); + return result; +} + +RLM_API realm_decimal128_t realm_dart_decimal128_copy(realm_decimal128_t x) { + return x; // work-around to Dart FFI issue +} + +RLM_API realm_decimal128_t realm_dart_decimal128_negate(realm_decimal128_t x) { + auto x_bid = to_BID_UINT128(x); + BID_UINT128 result; + bid128_negate(&result, &x_bid); + return to_decimal128(result); +} + +RLM_API realm_decimal128_t realm_dart_decimal128_add(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + BID_UINT128 result; + unsigned int flags = 0; + bid128_add(&result, &l, &r, &flags); + return to_decimal128(result); +} + +RLM_API realm_decimal128_t realm_dart_decimal128_subtract(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + BID_UINT128 result; + unsigned int flags = 0; + bid128_sub(&result, &l, &r, &flags); + return to_decimal128(result); +} + +RLM_API realm_decimal128_t realm_dart_decimal128_multiply(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + BID_UINT128 result; + unsigned int flags = 0; + bid128_mul(&result, &l, &r, &flags); + return to_decimal128(result); +} + +RLM_API realm_decimal128_t realm_dart_decimal128_divide(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + BID_UINT128 result; + unsigned int flags = 0; + bid128_div(&result, &l, &r, &flags); + return to_decimal128(result); +} + +RLM_API bool realm_dart_decimal128_equal(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + int result; + unsigned int flags = 0; + bid128_quiet_equal(&result, &l, &r, &flags); + return result; +} + +RLM_API bool realm_dart_decimal128_less_than(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + int result; + unsigned int flags = 0; + bid128_quiet_less(&result, &l, &r, &flags); + return result; +} + +RLM_API bool realm_dart_decimal128_greater_than(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + int result; + unsigned int flags = 0; + bid128_quiet_greater(&result, &l, &r, &flags); + return result; +} + +RLM_API int realm_dart_decimal128_compare_to(realm_decimal128_t x, realm_decimal128_t y) { + auto l = to_BID_UINT128(x); + auto r = to_BID_UINT128(y); + int lr, rl; + bid128_totalOrder(&lr, &l, &r); + bid128_totalOrder(&rl, &r, &l); + if (lr && rl) return 0; + if (lr) return -1; + if (rl) return 1; + return 0; +} diff --git a/src/realm_dart_decimal128.h b/src/realm_dart_decimal128.h new file mode 100644 index 000000000..5347de0b0 --- /dev/null +++ b/src/realm_dart_decimal128.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// Licensed 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. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_DART_DECIMAL128_H +#define REALM_DART_DECIMAL128_H + +#include + +RLM_API realm_decimal128_t realm_dart_decimal128_from_string(const char* string); +RLM_API realm_string_t realm_dart_decimal128_to_string(realm_decimal128_t x); + +RLM_API realm_decimal128_t realm_dart_decimal128_nan(); +RLM_API bool realm_dart_decimal128_is_nan(realm_decimal128_t x); +RLM_API realm_decimal128_t realm_dart_decimal128_from_int64(int64_t low); +RLM_API int64_t realm_dart_decimal128_to_int64(realm_decimal128_t x); +RLM_API realm_decimal128_t realm_dart_decimal128_negate(realm_decimal128_t x); +RLM_API realm_decimal128_t realm_dart_decimal128_add(realm_decimal128_t x, realm_decimal128_t y); +RLM_API realm_decimal128_t realm_dart_decimal128_subtract(realm_decimal128_t x, realm_decimal128_t y); +RLM_API realm_decimal128_t realm_dart_decimal128_multiply(realm_decimal128_t x, realm_decimal128_t y); +RLM_API realm_decimal128_t realm_dart_decimal128_divide(realm_decimal128_t x, realm_decimal128_t y); +RLM_API bool realm_dart_decimal128_equal(realm_decimal128_t x, realm_decimal128_t y); +RLM_API bool realm_dart_decimal128_less_than(realm_decimal128_t x, realm_decimal128_t y); +RLM_API bool realm_dart_decimal128_greater_than(realm_decimal128_t x, realm_decimal128_t y); +RLM_API int realm_dart_decimal128_compare_to(realm_decimal128_t x, realm_decimal128_t y); + +// work-around for Dart FFI issue +RLM_API realm_decimal128_t realm_dart_decimal128_copy(realm_decimal128_t x); + +#endif // REALM_DART_DECIMAL128_H \ No newline at end of file diff --git a/test/backlinks_test.dart b/test/backlinks_test.dart index 9f0650a35..bba22afea 100644 --- a/test/backlinks_test.dart +++ b/test/backlinks_test.dart @@ -16,7 +16,6 @@ // //////////////////////////////////////////////////////////////////////////////// -import 'package:realm_common/realm_common.dart'; import 'package:test/expect.dart'; import '../lib/realm.dart'; diff --git a/test/decimal128_test.dart b/test/decimal128_test.dart new file mode 100644 index 000000000..eab5de988 --- /dev/null +++ b/test/decimal128_test.dart @@ -0,0 +1,317 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// Licensed 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:math'; + +import 'package:meta/meta.dart'; +import 'package:test/expect.dart' hide throws; + +import '../lib/src/native/realm_core.dart'; +import 'test.dart'; + +const int defaultTimes = 100000; + +void repeat(dynamic Function() body, [int times = defaultTimes]) { + for (var i = 0; i < times; ++i) { + body(); + } +} + +@isTest +void repeatTest(String description, dynamic Function(Decimal128 x, int xInt, Decimal128 y, int yInt) body, [int times = defaultTimes]) { + final random = Random(42); // use a fixed seed to make tests deterministic + test(description, () { + repeat( + () { + // 2^31 ensures x * y doesn't overflow + var xInt = random.nextInt(1 << 31); + final x = Decimal128.fromInt(xInt); + var yInt = random.nextInt(1 << 31); + final y = Decimal128.fromInt(yInt); + + body(x, xInt, y, yInt); + }, + times, + ); + }); +} + +Future main([List? args]) async { + await setupTests(args); + + test('Decimal128.nan', () { + // Test that we mimic the behavior of Dart's double wrt. NaN and + // <, <=, >, >=. Unlike compareTo (which define a total order) these + // operators return false for NaN. + expect(0.0, isNot(double.nan)); + expect(0.0, isNot(lessThan(double.nan))); + expect(0.0, isNot(lessThanOrEqualTo(double.nan))); + expect(0.0, isNot(greaterThan(double.nan))); + expect(0.0, isNot(greaterThanOrEqualTo(double.nan))); + + expect(double.nan, isNot(0.0)); + expect(double.nan, isNot(lessThan(0.0))); + expect(double.nan, isNot(lessThanOrEqualTo(0.0))); + expect(double.nan, isNot(greaterThan(0.0))); + expect(double.nan, isNot(greaterThanOrEqualTo(0.0))); + + expect(double.nan, isNot(double.nan)); + expect(double.nan, isNot(lessThan(double.nan))); + expect(double.nan, isNot(lessThanOrEqualTo(double.nan))); + expect(double.nan, isNot(greaterThan(double.nan))); + expect(double.nan, isNot(greaterThanOrEqualTo(double.nan))); + + expect(double.nan.isNaN, isTrue); + expect((0.0).isNaN, isFalse); + expect((0.0 / 0.0).isNaN, isTrue); + expect((1.0).isNaN, isFalse); + expect((10.0).isNaN, isFalse); + expect(double.infinity.isNaN, isFalse); + + // NaN != NaN so compare as strings + expect(Decimal128.tryParse(Decimal128.nan.toString()).toString(), Decimal128.nan.toString()); + + expect(Decimal128.zero, isNot(Decimal128.nan)); + expect(Decimal128.zero, isNot(lessThan(Decimal128.nan))); + expect(Decimal128.zero, isNot(lessThanOrEqualTo(Decimal128.nan))); + expect(Decimal128.zero, isNot(greaterThan(Decimal128.nan))); + expect(Decimal128.zero, isNot(greaterThanOrEqualTo(Decimal128.nan))); + + expect(Decimal128.nan, isNot(Decimal128.zero)); + expect(Decimal128.nan, isNot(lessThan(Decimal128.zero))); + expect(Decimal128.nan, isNot(lessThanOrEqualTo(Decimal128.zero))); + expect(Decimal128.nan, isNot(greaterThan(Decimal128.zero))); + expect(Decimal128.nan, isNot(greaterThanOrEqualTo(Decimal128.zero))); + + expect(Decimal128.nan, isNot(Decimal128.nan)); + expect(Decimal128.nan, isNot(lessThan(Decimal128.nan))); + expect(Decimal128.nan, isNot(lessThanOrEqualTo(Decimal128.nan))); + expect(Decimal128.nan, isNot(greaterThan(Decimal128.nan))); + expect(Decimal128.nan, isNot(greaterThanOrEqualTo(Decimal128.nan))); + + expect(Decimal128.nan.isNaN, isTrue); + expect(Decimal128.zero.isNaN, isFalse); + expect((Decimal128.zero / Decimal128.zero).isNaN, isTrue); + expect(Decimal128.one.isNaN, isFalse); + expect(Decimal128.ten.isNaN, isFalse); + expect(Decimal128.infinity.isNaN, isFalse); + + // NaN != NaN so compare as strings + expect(Decimal128.tryParse(Decimal128.nan.toString()).toString(), Decimal128.nan.toString()); + }); + + test('Decimal128.infinity', () { + // Test that we mimic the behavior of Dart's double wrt. infinity + expect(double.tryParse(double.infinity.toString()), double.infinity); + expect(Decimal128.tryParse(Decimal128.infinity.toString()), Decimal128.infinity); + + expect(double.infinity, 1.0 / 0.0); + expect(Decimal128.infinity, Decimal128.one / Decimal128.zero); + + expect(double.infinity, double.parse('Infinity')); + expect(Decimal128.infinity, Decimal128.parse('Infinity')); + expect(Decimal128.infinity, Decimal128.parse('Inf')); // special for Decimal128 + + expect(double.infinity, double.parse('+Infinity')); + expect(Decimal128.infinity, Decimal128.parse('+Infinity')); + expect(Decimal128.infinity, Decimal128.parse('+Inf')); // special for Decimal128 + + expect(-double.infinity, double.negativeInfinity); + expect(-Decimal128.infinity, Decimal128.negativeInfinity); + + expect(-double.infinity, double.parse('-Infinity')); + expect(-Decimal128.infinity, Decimal128.parse('-Infinity')); + expect(-Decimal128.infinity, Decimal128.parse('-Inf')); // special for Decimal128 + }); + + test('Decimal128.parse throws on invalid input', () { + final inputs = [ + '', + ' 1', + 'a', + '1a', + '1.2.3', + '1,0', + ]; + for (var input in inputs) { + expect(() => Decimal128.parse(input), throwsFormatException); + } + }); + + test('Decimal128.tryParse', () { + final inputs = { + // input -> canonical output + '-5352089294466601279674461764E+87': '-5352089294466601279674461764E+87', + '-91.945E0542373228376880240736944': '-Inf', + '.60438002651113181e-0': '+60438002651113181E-17', + '100.0e-999': '+1000E-1000', + '100.0e-9999': '+0E-6176', + '855084089520e34934827269223590848': '+Inf', + 'NaN': '+NaN', + 'Inf': '+Inf', + 'Infinity': '+Inf', + '+Infinity': '+Inf', + '-Infinity': '-Inf', + 'Infi': null, + '-Infi': null, + '': null, + ' 1': null, + 'a': null, + '1a': null, + '1.2.3': null, + '1,0': null, + '1.0': '+10E-1', + }; + for (var entry in inputs.entries) { + final input = entry.key; + final output = entry.value; + expect(Decimal128.tryParse(input)?.toString(), output); + } + }); + + test('Decimal128 divide by zero', () { + // Test that we mimic the behavior of Dart's double when dividing by zero + expect(1.0 / 0.0, double.infinity); + expect(Decimal128.one / Decimal128.zero, Decimal128.infinity); + + expect(-1.0 / 0.0, -double.infinity); + expect(-Decimal128.one / Decimal128.zero, -Decimal128.infinity); + + expect(-1.0 / 0.0, double.negativeInfinity); + expect(-Decimal128.one / Decimal128.zero, Decimal128.negativeInfinity); + }); + + test('Decimal128 IEEE 754-2019 corner cases', () { + expect(double.infinity + 1, double.infinity); + expect(Decimal128.infinity + Decimal128.one, Decimal128.infinity); + + expect(double.infinity * -1, double.negativeInfinity); + expect(Decimal128.infinity * -Decimal128.one, Decimal128.negativeInfinity); + + expect((double.infinity * 0).isNaN, isTrue); + expect((Decimal128.infinity * Decimal128.zero).isNaN, isTrue); + }); + + test('Decimal128.compareTo is a total ordering', () { + // Test that we mimic the behavior of Dart's double wrt. compareTo in relation + // to IEEE754-2019, ie. +/- NaN, +/- infinity and +/- zero and define a total + // ordering. + expect((-0.0).compareTo(0.0), -1); + expect((0.0).compareTo(-0.0), 1); + expect((0.0).compareTo(0.0), 0); + expect((-0.0).compareTo(-0.0), 0); + + expect(double.negativeInfinity.compareTo(double.infinity), -1); + expect(double.infinity.compareTo(double.negativeInfinity), 1); + expect(double.infinity.compareTo(double.infinity), 0); + expect(double.negativeInfinity.compareTo(double.negativeInfinity), 0); + + expect((1.0).compareTo(double.nan), -1); + expect(double.nan.compareTo(double.nan), 0); + expect(double.nan.compareTo(1.0), 1); + + // .. and now for Decimal128 + expect((-Decimal128.zero).compareTo(Decimal128.zero), -1); + expect(Decimal128.zero.compareTo(-Decimal128.zero), 1); + expect(Decimal128.zero.compareTo(Decimal128.zero), 0); + expect((-Decimal128.zero).compareTo(-Decimal128.zero), 0); + + expect(Decimal128.negativeInfinity.compareTo(Decimal128.infinity), -1); + expect(Decimal128.infinity.compareTo(Decimal128.negativeInfinity), 1); + expect(Decimal128.infinity.compareTo(Decimal128.infinity), 0); + expect(Decimal128.negativeInfinity.compareTo(Decimal128.negativeInfinity), 0); + + expect(Decimal128.one.compareTo(Decimal128.nan), -1); + expect(Decimal128.nan.compareTo(Decimal128.nan), 0); + expect(Decimal128.nan.compareTo(Decimal128.one), 1); + }); + + repeatTest('Decimal128.compareTo + <, <=, ==, !=, >=, >', (x, xInt, y, yInt) { + expect(x.compareTo(x), 0); + expect(x.compareTo(y), -(y.compareTo(x))); + expect(x.compareTo(y), xInt.compareTo(yInt)); + + expect(x < x, isFalse); + expect(x < y, y > x); + expect(x < y, xInt < yInt); + + expect(x <= x, isTrue); + expect(x <= y, y >= x); + expect(x <= y, xInt <= yInt); + + expect(x == x, isTrue); + expect(x == y, y == x); + expect(x == y, xInt == yInt); + + expect(x != x, isFalse); + expect(x != y, y != x); + expect(x != y, xInt != yInt); + + expect(x > x, isFalse); + expect(x > y, y < x); + expect(x > y, xInt > yInt); + + expect(x >= x, isTrue); + expect(x >= y, y <= x); + expect(x >= y, xInt >= yInt); + }); + + repeatTest('Decimal128.toInt/fromInt roundtrip', (x, xInt, y, yInt) { + expect(Decimal128.fromInt(x.toInt()), x); + }); + + repeatTest('Decimal128.fromInt/toInt roundtrip', (x, xInt, y, yInt) { + expect(x.toInt(), xInt); + }); + + repeatTest('Decimal128.toString/parse roundtrip', (x, xInt, y, yInt) { + expect(Decimal128.parse(x.toString()), x); + }); + + repeatTest('Decimal128 add', (x, xInt, y, yInt) { + expect(x + y, Decimal128.fromInt(xInt + yInt)); + }); + + repeatTest('Decimal128 subtract', (x, xInt, y, yInt) { + expect(x - y, Decimal128.fromInt(xInt - yInt)); + }); + + repeatTest('Decimal128 multiply', (x, xInt, y, yInt) { + expect(x * y, Decimal128.fromInt(xInt * yInt)); + }); + + repeatTest('Decimal128 multiply & divide', (x, xInt, y, yInt) { + expect((x * y) / x, y); + expect((x * y) / y, x); + }); + + final epsilon = Decimal128.one / Decimal128.fromInt(1 << 62); + repeatTest('Decimal128 divide', (x, xInt, y, yInt) { + expect((x / y - (Decimal128.one / (y / x))).abs(), lessThan(epsilon)); + }); + + repeatTest('Decimal128 negate', (x, xInt, y, yInt) { + expect((-x).toInt(), -xInt); + expect(-(-x), x); + }); + + repeatTest('Decimal128.abs', (x, xInt, y, yInt) { + expect(x.abs(), (-x).abs()); + expect(x.abs(), x.abs().abs()); // abs is idempotent + }); +} diff --git a/test/dynamic_realm_test.dart b/test/dynamic_realm_test.dart index da0370ca3..c03245368 100644 --- a/test/dynamic_realm_test.dart +++ b/test/dynamic_realm_test.dart @@ -87,16 +87,17 @@ Future main([List? args]) async { final objectId = ObjectId(); final uuid = Uuid.v4(); - AllTypes _getPopulatedAllTypes() => AllTypes('abc', true, date, -123.456, objectId, uuid, -987, + AllTypes _getPopulatedAllTypes() => AllTypes('abc', true, date, -123.456, objectId, uuid, -987, Decimal128.fromDouble(42), nullableStringProp: 'def', nullableBoolProp: true, nullableDateProp: date, nullableDoubleProp: -123.456, nullableObjectIdProp: objectId, nullableUuidProp: uuid, - nullableIntProp: 123); + nullableIntProp: 123, + nullableDecimalProp: Decimal128.fromDouble(4242)); - AllTypes _getEmptyAllTypes() => AllTypes('', false, DateTime(0).toUtc(), 0, objectId, uuid, 0); + AllTypes _getEmptyAllTypes() => AllTypes('', false, DateTime(0).toUtc(), 0, objectId, uuid, 0, Decimal128.zero); AllCollections _getPopulatedAllCollections() => AllCollections( strings: ['abc', 'def'], @@ -143,6 +144,11 @@ Future main([List? args]) async { expect(actual.dynamic.get('nullableIntProp'), expected.nullableIntProp); expect(actual.dynamic.get('nullableIntProp'), expected.nullableIntProp); + expect(actual.dynamic.get('decimalProp'), expected.decimalProp); + expect(actual.dynamic.get('decimalProp'), expected.decimalProp); + expect(actual.dynamic.get('nullableDecimalProp'), expected.nullableDecimalProp); + expect(actual.dynamic.get('nullableDecimalProp'), expected.nullableDecimalProp); + dynamic actualDynamic = actual; expect(actualDynamic.stringProp, expected.stringProp); expect(actualDynamic.nullableStringProp, expected.nullableStringProp); @@ -158,6 +164,8 @@ Future main([List? args]) async { expect(actualDynamic.nullableUuidProp, expected.nullableUuidProp); expect(actualDynamic.intProp, expected.intProp); expect(actualDynamic.nullableIntProp, expected.nullableIntProp); + expect(actualDynamic.decimalProp, expected.decimalProp); + expect(actualDynamic.nullableDecimalProp, expected.nullableDecimalProp); } void _validateDynamicLists(RealmObject actual, AllCollections expected) { @@ -182,6 +190,9 @@ Future main([List? args]) async { expect(actual.dynamic.getList('ints'), expected.ints); expect(actual.dynamic.getList('ints'), expected.ints); + expect(actual.dynamic.getList('decimals'), expected.decimals); + expect(actual.dynamic.getList('decimals'), expected.decimals); + dynamic actualDynamic = actual; expect(actualDynamic.strings, expected.strings); expect(actualDynamic.bools, expected.bools); @@ -190,6 +201,7 @@ Future main([List? args]) async { expect(actualDynamic.objectIds, expected.objectIds); expect(actualDynamic.uuids, expected.uuids); expect(actualDynamic.ints, expected.ints); + expect(actualDynamic.decimals, expected.decimals); } for (var isDynamic in [true, false]) { diff --git a/test/embedded_test.dart b/test/embedded_test.dart index 9c3bb2eaa..f8c27469e 100644 --- a/test/embedded_test.dart +++ b/test/embedded_test.dart @@ -68,7 +68,7 @@ Future main([List? args]) async { final oid = ObjectId(); final uuid = Uuid.v4(); realm.write(() { - realm.add(ObjectWithEmbedded('abc', singleObject: AllTypesEmbedded('str', true, now, 1.23, oid, uuid, 99))); + realm.add(ObjectWithEmbedded('abc', singleObject: AllTypesEmbedded('str', true, now, 1.23, oid, uuid, 99, Decimal128.ten))); }); final obj = realm.all().single; @@ -81,6 +81,7 @@ Future main([List? args]) async { expect(json, contains('"objectIdProp":"$oid"')); expect(json, contains('"uuidProp":"$uuid"')); expect(json, contains('"intProp":99')); + expect(json, contains('"decimalProp":"10"')); // note the quotes! expect(json, contains('"nullableStringProp":null')); expect(json, contains('"nullableBoolProp":null')); @@ -89,6 +90,7 @@ Future main([List? args]) async { expect(json, contains('"nullableObjectIdProp":null')); expect(json, contains('"nullableUuidProp":null')); expect(json, contains('"nullableIntProp":null')); + expect(json, contains('"nullableDecimalProp":null')); }); test('Embedded object get/set properties', () { diff --git a/test/realm_object_test.dart b/test/realm_object_test.dart index 26e10255d..6dd476bab 100644 --- a/test/realm_object_test.dart +++ b/test/realm_object_test.dart @@ -448,7 +448,7 @@ Future main([List? args]) async { final config = Configuration.local([AllTypes.schema]); final realm = getRealm(config); final obj = realm.write(() { - return realm.add(AllTypes('', false, date, 0, ObjectId(), Uuid.v4(), 0)); + return realm.add(AllTypes('', false, date, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); }); final json = obj.toJson(); @@ -490,7 +490,7 @@ Future main([List? args]) async { expect(date.isUtc, isFalse); final obj = realm.write(() { - return realm.add(AllTypes('', false, date, 0, ObjectId(), Uuid.v4(), 0)); + return realm.add(AllTypes('', false, date, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); }); final json = obj.toJson(); @@ -507,8 +507,8 @@ Future main([List? args]) async { final date = DateTime.now(); realm.write(() { - realm.add(AllTypes('abc', false, date, 0, ObjectId(), Uuid.v4(), 0)); - realm.add(AllTypes('cde', false, DateTime.now().add(Duration(seconds: 1)), 0, ObjectId(), Uuid.v4(), 0)); + realm.add(AllTypes('abc', false, date, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); + realm.add(AllTypes('cde', false, DateTime.now().add(Duration(seconds: 1)), 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); }); var results = realm.all().query('dateProp = \$0', [date]); @@ -525,9 +525,9 @@ Future main([List? args]) async { final date3 = date1.subtract(Duration(microseconds: 1)); realm.write(() { - realm.add(AllTypes('1', false, date1, 0, ObjectId(), Uuid.v4(), 0)); - realm.add(AllTypes('2', false, date2, 0, ObjectId(), Uuid.v4(), 0)); - realm.add(AllTypes('3', false, date3, 0, ObjectId(), Uuid.v4(), 0)); + realm.add(AllTypes('1', false, date1, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); + realm.add(AllTypes('2', false, date2, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); + realm.add(AllTypes('3', false, date3, 0, ObjectId(), Uuid.v4(), 0, Decimal128.one)); }); final lessThan1 = realm.all().query('dateProp < \$0', [date1]); @@ -552,7 +552,7 @@ Future main([List? args]) async { var uuid = Uuid.v4(); final object = realm.write(() { - return realm.add(AllTypes('cde', false, date, 0.1, objectId, uuid, 4)); + return realm.add(AllTypes('cde', false, date, 0.1, objectId, uuid, 4, Decimal128.ten)); }); expect(object.stringProp, 'cde'); @@ -562,6 +562,7 @@ Future main([List? args]) async { expect(object.objectIdProp, objectId); expect(object.uuidProp, uuid); expect(object.intProp, 4); + expect(object.decimalProp, Decimal128.ten); date = DateTime.now().add(Duration(days: 1)).toUtc(); objectId = ObjectId(); @@ -574,6 +575,7 @@ Future main([List? args]) async { object.objectIdProp = objectId; object.uuidProp = uuid; object.intProp = 5; + object.decimalProp = Decimal128.one; }); expect(object.stringProp, 'abc'); @@ -583,6 +585,7 @@ Future main([List? args]) async { expect(object.objectIdProp, objectId); expect(object.uuidProp, uuid); expect(object.intProp, 5); + expect(object.decimalProp, Decimal128.one); }); test('RealmObject.freeze when typed returns typed frozen object', () { diff --git a/test/realm_value_test.dart b/test/realm_value_test.dart index 77302cd5f..d8c0ffae0 100644 --- a/test/realm_value_test.dart +++ b/test/realm_value_test.dart @@ -58,6 +58,7 @@ Future main([List? args]) async { now, ObjectId.fromTimestamp(now), Uuid.v4(), + Decimal128.fromDouble(128.128), ]; for (final x in values) { @@ -118,6 +119,9 @@ Future main([List? args]) async { case Uuid: expect(value, isA()); break; + case Decimal128: + expect(value, isA()); + break; default: fail('${something.oneAny} not handled correctly in switch'); } @@ -145,6 +149,8 @@ Future main([List? args]) async { expect(type, Uuid); } else if (value is ObjectId) { expect(type, ObjectId); + } else if (value is Decimal128) { + expect(type, Decimal128); } else if (value is AnythingGoes) { expect(type, AnythingGoes); } else if (value is Stuff) { @@ -200,6 +206,7 @@ Future main([List? args]) async { now, ObjectId.fromTimestamp(now), Uuid.v4(), + Decimal128.fromInt(128), ]; final config = Configuration.local([AnythingGoes.schema, Stuff.schema]); diff --git a/test/results_test.dart b/test/results_test.dart index 2008247b1..5ec2ae34d 100644 --- a/test/results_test.dart +++ b/test/results_test.dart @@ -662,4 +662,26 @@ Future main([List? args]) async { if (count > 1) fail('Should only receive one event'); } }); + + test('query by condition on decimal128', () { + final config = Configuration.local([ObjectWithDecimal.schema]); + final realm = getRealm(config); + + final small = Decimal128.fromInt(1); + final big = Decimal128.fromInt(1 << 62) * Decimal128.fromInt(1 << 62); + + realm.write(() => realm.addAll([ + ObjectWithDecimal(small), + ObjectWithDecimal(big), + ])); + + expect(realm.query(r'decimal < $0', [small]), isEmpty); + expect(realm.query(r'decimal < $0', [big]).map((e) => e.decimal), [small]); + expect(realm.query(r'decimal <= $0', [big]).map((e) => e.decimal), [small, big]); + expect(realm.query(r'decimal == $0', [small]).map((e) => e.decimal), [small]); + expect(realm.query(r'decimal == $0', [big]).map((e) => e.decimal), [big]); + expect(realm.query(r'decimal > $0', [big]), isEmpty); + expect(realm.query(r'decimal > $0', [small]).map((e) => e.decimal), [big]); + expect(realm.query(r'decimal >= $0', [small]).map((e) => e.decimal), [small, big]); + }); } diff --git a/test/test.dart b/test/test.dart index fc0fcb8b0..465b47b4f 100644 --- a/test/test.dart +++ b/test/test.dart @@ -124,6 +124,7 @@ class _AllTypes { late ObjectId objectIdProp; late Uuid uuidProp; late int intProp; + late Decimal128 decimalProp; late String? nullableStringProp; late bool? nullableBoolProp; @@ -132,6 +133,7 @@ class _AllTypes { late ObjectId? nullableObjectIdProp; late Uuid? nullableUuidProp; late int? nullableIntProp; + late Decimal128? nullableDecimalProp; } @RealmModel() @@ -152,6 +154,7 @@ class _AllCollections { late List objectIds; late List uuids; late List ints; + late List decimals; late List nullableStrings; late List nullableBools; @@ -160,6 +163,7 @@ class _AllCollections { late List nullableObjectIds; late List nullableUuids; late List nullableInts; + late List nullableDecimals; } @RealmModel() @@ -177,6 +181,7 @@ class _NullableTypes { late ObjectId? objectIdProp; late Uuid? uuidProp; late int? intProp; + late Decimal128? decimalProp; } @RealmModel() @@ -240,6 +245,7 @@ class _AllTypesEmbedded { late ObjectId objectIdProp; late Uuid uuidProp; late int intProp; + late Decimal128 decimalProp; late String? nullableStringProp; late bool? nullableBoolProp; @@ -248,6 +254,7 @@ class _AllTypesEmbedded { late ObjectId? nullableObjectIdProp; late Uuid? nullableUuidProp; late int? nullableIntProp; + late Decimal128? nullableDecimalProp; late List strings; late List bools; @@ -256,6 +263,7 @@ class _AllTypesEmbedded { late List objectIds; late List uuids; late List ints; + late List decimals; } @RealmModel() @@ -300,6 +308,12 @@ class _RecursiveEmbedded3 { late String value; } +@RealmModel() +class _ObjectWithDecimal { + late Decimal128 decimal; + Decimal128? nullableDecimal; +} + String? testName; Map arguments = {}; final baasApps = {}; diff --git a/test/test.g.dart b/test/test.g.dart index 91b2e875f..7805d4c71 100644 --- a/test/test.g.dart +++ b/test/test.g.dart @@ -482,7 +482,8 @@ class AllTypes extends _AllTypes double doubleProp, ObjectId objectIdProp, Uuid uuidProp, - int intProp, { + int intProp, + Decimal128 decimalProp, { String? nullableStringProp, bool? nullableBoolProp, DateTime? nullableDateProp, @@ -490,6 +491,7 @@ class AllTypes extends _AllTypes ObjectId? nullableObjectIdProp, Uuid? nullableUuidProp, int? nullableIntProp, + Decimal128? nullableDecimalProp, }) { RealmObjectBase.set(this, 'stringProp', stringProp); RealmObjectBase.set(this, 'boolProp', boolProp); @@ -498,6 +500,7 @@ class AllTypes extends _AllTypes RealmObjectBase.set(this, 'objectIdProp', objectIdProp); RealmObjectBase.set(this, 'uuidProp', uuidProp); RealmObjectBase.set(this, 'intProp', intProp); + RealmObjectBase.set(this, 'decimalProp', decimalProp); RealmObjectBase.set(this, 'nullableStringProp', nullableStringProp); RealmObjectBase.set(this, 'nullableBoolProp', nullableBoolProp); RealmObjectBase.set(this, 'nullableDateProp', nullableDateProp); @@ -505,6 +508,7 @@ class AllTypes extends _AllTypes RealmObjectBase.set(this, 'nullableObjectIdProp', nullableObjectIdProp); RealmObjectBase.set(this, 'nullableUuidProp', nullableUuidProp); RealmObjectBase.set(this, 'nullableIntProp', nullableIntProp); + RealmObjectBase.set(this, 'nullableDecimalProp', nullableDecimalProp); } AllTypes._(); @@ -551,6 +555,13 @@ class AllTypes extends _AllTypes @override set intProp(int value) => RealmObjectBase.set(this, 'intProp', value); + @override + Decimal128 get decimalProp => + RealmObjectBase.get(this, 'decimalProp') as Decimal128; + @override + set decimalProp(Decimal128 value) => + RealmObjectBase.set(this, 'decimalProp', value); + @override String? get nullableStringProp => RealmObjectBase.get(this, 'nullableStringProp') as String?; @@ -600,6 +611,14 @@ class AllTypes extends _AllTypes set nullableIntProp(int? value) => RealmObjectBase.set(this, 'nullableIntProp', value); + @override + Decimal128? get nullableDecimalProp => + RealmObjectBase.get(this, 'nullableDecimalProp') + as Decimal128?; + @override + set nullableDecimalProp(Decimal128? value) => + RealmObjectBase.set(this, 'nullableDecimalProp', value); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -619,6 +638,7 @@ class AllTypes extends _AllTypes SchemaProperty('objectIdProp', RealmPropertyType.objectid), SchemaProperty('uuidProp', RealmPropertyType.uuid), SchemaProperty('intProp', RealmPropertyType.int), + SchemaProperty('decimalProp', RealmPropertyType.decimal128), SchemaProperty('nullableStringProp', RealmPropertyType.string, optional: true), SchemaProperty('nullableBoolProp', RealmPropertyType.bool, @@ -632,6 +652,8 @@ class AllTypes extends _AllTypes SchemaProperty('nullableUuidProp', RealmPropertyType.uuid, optional: true), SchemaProperty('nullableIntProp', RealmPropertyType.int, optional: true), + SchemaProperty('nullableDecimalProp', RealmPropertyType.decimal128, + optional: true), ]); } } @@ -702,6 +724,7 @@ class AllCollections extends _AllCollections Iterable objectIds = const [], Iterable uuids = const [], Iterable ints = const [], + Iterable decimals = const [], Iterable nullableStrings = const [], Iterable nullableBools = const [], Iterable nullableDates = const [], @@ -709,6 +732,7 @@ class AllCollections extends _AllCollections Iterable nullableObjectIds = const [], Iterable nullableUuids = const [], Iterable nullableInts = const [], + Iterable nullableDecimals = const [], }) { RealmObjectBase.set>( this, 'strings', RealmList(strings)); @@ -721,6 +745,8 @@ class AllCollections extends _AllCollections this, 'objectIds', RealmList(objectIds)); RealmObjectBase.set>(this, 'uuids', RealmList(uuids)); RealmObjectBase.set>(this, 'ints', RealmList(ints)); + RealmObjectBase.set>( + this, 'decimals', RealmList(decimals)); RealmObjectBase.set>( this, 'nullableStrings', RealmList(nullableStrings)); RealmObjectBase.set>( @@ -735,6 +761,8 @@ class AllCollections extends _AllCollections this, 'nullableUuids', RealmList(nullableUuids)); RealmObjectBase.set>( this, 'nullableInts', RealmList(nullableInts)); + RealmObjectBase.set>( + this, 'nullableDecimals', RealmList(nullableDecimals)); } AllCollections._(); @@ -787,6 +815,14 @@ class AllCollections extends _AllCollections @override set ints(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmList get decimals => + RealmObjectBase.get(this, 'decimals') + as RealmList; + @override + set decimals(covariant RealmList value) => + throw RealmUnsupportedSetError(); + @override RealmList get nullableStrings => RealmObjectBase.get(this, 'nullableStrings') @@ -840,6 +876,14 @@ class AllCollections extends _AllCollections set nullableInts(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmList get nullableDecimals => + RealmObjectBase.get(this, 'nullableDecimals') + as RealmList; + @override + set nullableDecimals(covariant RealmList value) => + throw RealmUnsupportedSetError(); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -867,6 +911,8 @@ class AllCollections extends _AllCollections collectionType: RealmCollectionType.list), SchemaProperty('ints', RealmPropertyType.int, collectionType: RealmCollectionType.list), + SchemaProperty('decimals', RealmPropertyType.decimal128, + collectionType: RealmCollectionType.list), SchemaProperty('nullableStrings', RealmPropertyType.string, optional: true, collectionType: RealmCollectionType.list), SchemaProperty('nullableBools', RealmPropertyType.bool, @@ -881,6 +927,8 @@ class AllCollections extends _AllCollections optional: true, collectionType: RealmCollectionType.list), SchemaProperty('nullableInts', RealmPropertyType.int, optional: true, collectionType: RealmCollectionType.list), + SchemaProperty('nullableDecimals', RealmPropertyType.decimal128, + optional: true, collectionType: RealmCollectionType.list), ]); } } @@ -897,6 +945,7 @@ class NullableTypes extends _NullableTypes ObjectId? objectIdProp, Uuid? uuidProp, int? intProp, + Decimal128? decimalProp, }) { RealmObjectBase.set(this, '_id', id); RealmObjectBase.set(this, 'differentiator', differentiator); @@ -907,6 +956,7 @@ class NullableTypes extends _NullableTypes RealmObjectBase.set(this, 'objectIdProp', objectIdProp); RealmObjectBase.set(this, 'uuidProp', uuidProp); RealmObjectBase.set(this, 'intProp', intProp); + RealmObjectBase.set(this, 'decimalProp', decimalProp); } NullableTypes._(); @@ -965,6 +1015,13 @@ class NullableTypes extends _NullableTypes @override set intProp(int? value) => RealmObjectBase.set(this, 'intProp', value); + @override + Decimal128? get decimalProp => + RealmObjectBase.get(this, 'decimalProp') as Decimal128?; + @override + set decimalProp(Decimal128? value) => + RealmObjectBase.set(this, 'decimalProp', value); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -989,6 +1046,8 @@ class NullableTypes extends _NullableTypes optional: true), SchemaProperty('uuidProp', RealmPropertyType.uuid, optional: true), SchemaProperty('intProp', RealmPropertyType.int, optional: true), + SchemaProperty('decimalProp', RealmPropertyType.decimal128, + optional: true), ]); } } @@ -1338,7 +1397,8 @@ class AllTypesEmbedded extends _AllTypesEmbedded double doubleProp, ObjectId objectIdProp, Uuid uuidProp, - int intProp, { + int intProp, + Decimal128 decimalProp, { String? nullableStringProp, bool? nullableBoolProp, DateTime? nullableDateProp, @@ -1346,6 +1406,7 @@ class AllTypesEmbedded extends _AllTypesEmbedded ObjectId? nullableObjectIdProp, Uuid? nullableUuidProp, int? nullableIntProp, + Decimal128? nullableDecimalProp, Iterable strings = const [], Iterable bools = const [], Iterable dates = const [], @@ -1353,6 +1414,7 @@ class AllTypesEmbedded extends _AllTypesEmbedded Iterable objectIds = const [], Iterable uuids = const [], Iterable ints = const [], + Iterable decimals = const [], }) { RealmObjectBase.set(this, 'stringProp', stringProp); RealmObjectBase.set(this, 'boolProp', boolProp); @@ -1361,6 +1423,7 @@ class AllTypesEmbedded extends _AllTypesEmbedded RealmObjectBase.set(this, 'objectIdProp', objectIdProp); RealmObjectBase.set(this, 'uuidProp', uuidProp); RealmObjectBase.set(this, 'intProp', intProp); + RealmObjectBase.set(this, 'decimalProp', decimalProp); RealmObjectBase.set(this, 'nullableStringProp', nullableStringProp); RealmObjectBase.set(this, 'nullableBoolProp', nullableBoolProp); RealmObjectBase.set(this, 'nullableDateProp', nullableDateProp); @@ -1368,6 +1431,7 @@ class AllTypesEmbedded extends _AllTypesEmbedded RealmObjectBase.set(this, 'nullableObjectIdProp', nullableObjectIdProp); RealmObjectBase.set(this, 'nullableUuidProp', nullableUuidProp); RealmObjectBase.set(this, 'nullableIntProp', nullableIntProp); + RealmObjectBase.set(this, 'nullableDecimalProp', nullableDecimalProp); RealmObjectBase.set>( this, 'strings', RealmList(strings)); RealmObjectBase.set>(this, 'bools', RealmList(bools)); @@ -1379,6 +1443,8 @@ class AllTypesEmbedded extends _AllTypesEmbedded this, 'objectIds', RealmList(objectIds)); RealmObjectBase.set>(this, 'uuids', RealmList(uuids)); RealmObjectBase.set>(this, 'ints', RealmList(ints)); + RealmObjectBase.set>( + this, 'decimals', RealmList(decimals)); } AllTypesEmbedded._(); @@ -1425,6 +1491,13 @@ class AllTypesEmbedded extends _AllTypesEmbedded @override set intProp(int value) => RealmObjectBase.set(this, 'intProp', value); + @override + Decimal128 get decimalProp => + RealmObjectBase.get(this, 'decimalProp') as Decimal128; + @override + set decimalProp(Decimal128 value) => + RealmObjectBase.set(this, 'decimalProp', value); + @override String? get nullableStringProp => RealmObjectBase.get(this, 'nullableStringProp') as String?; @@ -1474,6 +1547,14 @@ class AllTypesEmbedded extends _AllTypesEmbedded set nullableIntProp(int? value) => RealmObjectBase.set(this, 'nullableIntProp', value); + @override + Decimal128? get nullableDecimalProp => + RealmObjectBase.get(this, 'nullableDecimalProp') + as Decimal128?; + @override + set nullableDecimalProp(Decimal128? value) => + RealmObjectBase.set(this, 'nullableDecimalProp', value); + @override RealmList get strings => RealmObjectBase.get(this, 'strings') as RealmList; @@ -1522,6 +1603,14 @@ class AllTypesEmbedded extends _AllTypesEmbedded @override set ints(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmList get decimals => + RealmObjectBase.get(this, 'decimals') + as RealmList; + @override + set decimals(covariant RealmList value) => + throw RealmUnsupportedSetError(); + @override Stream> get changes => RealmObjectBase.getChanges(this); @@ -1543,6 +1632,7 @@ class AllTypesEmbedded extends _AllTypesEmbedded SchemaProperty('objectIdProp', RealmPropertyType.objectid), SchemaProperty('uuidProp', RealmPropertyType.uuid), SchemaProperty('intProp', RealmPropertyType.int), + SchemaProperty('decimalProp', RealmPropertyType.decimal128), SchemaProperty('nullableStringProp', RealmPropertyType.string, optional: true), SchemaProperty('nullableBoolProp', RealmPropertyType.bool, @@ -1556,6 +1646,8 @@ class AllTypesEmbedded extends _AllTypesEmbedded SchemaProperty('nullableUuidProp', RealmPropertyType.uuid, optional: true), SchemaProperty('nullableIntProp', RealmPropertyType.int, optional: true), + SchemaProperty('nullableDecimalProp', RealmPropertyType.decimal128, + optional: true), SchemaProperty('strings', RealmPropertyType.string, collectionType: RealmCollectionType.list), SchemaProperty('bools', RealmPropertyType.bool, @@ -1570,6 +1662,8 @@ class AllTypesEmbedded extends _AllTypesEmbedded collectionType: RealmCollectionType.list), SchemaProperty('ints', RealmPropertyType.int, collectionType: RealmCollectionType.list), + SchemaProperty('decimals', RealmPropertyType.decimal128, + collectionType: RealmCollectionType.list), ]); } } @@ -1848,3 +1942,49 @@ class RecursiveEmbedded3 extends _RecursiveEmbedded3 ]); } } + +class ObjectWithDecimal extends _ObjectWithDecimal + with RealmEntity, RealmObjectBase, RealmObject { + ObjectWithDecimal( + Decimal128 decimal, { + Decimal128? nullableDecimal, + }) { + RealmObjectBase.set(this, 'decimal', decimal); + RealmObjectBase.set(this, 'nullableDecimal', nullableDecimal); + } + + ObjectWithDecimal._(); + + @override + Decimal128 get decimal => + RealmObjectBase.get(this, 'decimal') as Decimal128; + @override + set decimal(Decimal128 value) => RealmObjectBase.set(this, 'decimal', value); + + @override + Decimal128? get nullableDecimal => + RealmObjectBase.get(this, 'nullableDecimal') as Decimal128?; + @override + set nullableDecimal(Decimal128? value) => + RealmObjectBase.set(this, 'nullableDecimal', value); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + ObjectWithDecimal freeze() => + RealmObjectBase.freezeObject(this); + + static SchemaObject get schema => _schema ??= _initSchema(); + static SchemaObject? _schema; + static SchemaObject _initSchema() { + RealmObjectBase.registerFactory(ObjectWithDecimal._); + return const SchemaObject( + ObjectType.realmObject, ObjectWithDecimal, 'ObjectWithDecimal', [ + SchemaProperty('decimal', RealmPropertyType.decimal128), + SchemaProperty('nullableDecimal', RealmPropertyType.decimal128, + optional: true), + ]); + } +}