Skip to content

Commit

Permalink
Override RealmResults.indexOf and .contains with efficient implementa…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
nielsenko committed Sep 1, 2023
1 parent 1116e42 commit d5168f3
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
17 changes: 17 additions & 0 deletions lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,23 @@ class _RealmCore {
});
}

int resultsFind(RealmResults results, Object? value) {
return using((Arena arena) {
final out_index = arena<Size>();
final out_found = arena<Bool>();
final realm_value = _toRealmValue(value, arena);
_realmLib.invokeGetBool(
() => _realmLib.realm_results_find(
results.handle._pointer,
realm_value,
out_index,
out_found,
),
);
return out_found.value ? out_index.value : -1;
});
}

RealmObjectHandle resultsGetObjectAt(RealmResults results, int index) {
final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_get_object(results.handle._pointer, index));
return RealmObjectHandle._(pointer, results.realm.handle);
Expand Down
19 changes: 19 additions & 0 deletions lib/src/results.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ class RealmResults<T extends Object?> extends Iterable<T> with RealmEntity imple
}
}

@pragma('vm:prefer-inline')
int _indexOf(T element, int start, String methodName) {
if (element is RealmObjectBase && !element.isManaged) {
throw RealmStateError('Cannot call $methodName on a results with an element that is an unmanaged object');
}
if (start < 0) start = 0;
start += _skipOffset;
final index = realmCore.resultsFind(this, element);
return index < start ? -1 : index; // to align with dart list semantics
}

/// Returns the `index` of the first occurrence of the specified [element] in this collection,
/// or `-1` if the collection does not contain the element.
int indexOf(covariant T element, [int start = 0]) => _indexOf(element, start, 'indexOf');

/// `true` if the `Results` collection contains the specified [element].
@override
bool contains(covariant T element) => _indexOf(element, 0, 'contains') >= 0;

/// `true` if the `Results` collection is empty.
@override
bool get isEmpty => length == 0;
Expand Down
46 changes: 46 additions & 0 deletions test/results_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -968,4 +968,50 @@ Future<void> main([List<String>? args]) async {
// you are not supposed to call current, if moveNext return false
expect(() => rit.current, throwsA(isA<RealmException>()));
});

test('RealmResults.indexOf', () {
final config = Configuration.local([Task.schema]);
final realm = getRealm(config);
const max = 10;
realm.write(() {
realm.addAll(List.generate(max, (_) => Task(ObjectId())));
});

final results = realm.all<Task>();
expect(() => results.indexOf(Task(ObjectId())), throws<RealmStateError>());
int i = 0;
for (final t in results) {
expect(results.indexOf(t), i++);
}
});

test('RealmResults.contains', () {
final config = Configuration.local([Task.schema]);
final realm = getRealm(config);
const max = 10;
final tasks = List.generate(max, (_) => Task(ObjectId()));
realm.write(() {
realm.addAll(tasks);
});

final all = realm.all<Task>();
final none = realm.query<Task>('FALSEPREDICATE');

expect(() => all.contains(Task(ObjectId())), throws<RealmStateError>());
// ignore: unnecessary_cast, iterable_contains_unrelated_type
expect(() => (all as Iterable<Task>).contains(1), throwsA(isA<TypeError>()));

int i = 0;
for (final t in all) {
expect(all.contains(t), isTrue);
expect(none.contains(t), isFalse);
}

for (int i = 0; i < max; i++) {
for (var j = 0; j < max - i; j++) {
expect(all.skip(i + j).contains(tasks[i + j]), true);
expect(all.skip(i + j + 1).contains(tasks[i + j]), false);
}
}
});
}

0 comments on commit d5168f3

Please sign in to comment.