Skip to content

Commit 468bd24

Browse files
authored
Merge pull request #96 from objectbox/dev
v0.6.2 release
2 parents 17fc5e1 + 659947d commit 468bd24

File tree

13 files changed

+207
-32
lines changed

13 files changed

+207
-32
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
0.6.2 (2020-03-09)
2+
------------------
3+
* Support large object arrays on 32-bit platforms/emulators.
4+
15
0.6.1 (2020-01-23)
26
------------------
37
* Fix Flutter Android/iOS release build failures

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ Installation
1616
Add the following dependencies to your `pubspec.yaml`:
1717
```yaml
1818
dependencies:
19-
objectbox: ^0.6.1
19+
objectbox: ^0.6.2
2020

2121
dev_dependencies:
2222
build_runner: ^1.0.0
23-
objectbox_generator: ^0.6.1
23+
objectbox_generator: ^0.6.2
2424
```
2525
2626
Proceed based on whether you're developing a Flutter app or a standalone dart program:

example/flutter/objectbox_demo/pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ dependencies:
1111
cupertino_icons: ^0.1.2
1212
path_provider: any
1313
intl: any
14-
objectbox: ^0.6.1
14+
objectbox: ^0.6.2
1515

1616
dev_dependencies:
1717
flutter_test:
1818
sdk: flutter
1919
build_runner: ^1.0.0
20-
objectbox_generator: ^0.6.1
20+
objectbox_generator: ^0.6.2
2121

2222
flutter:
2323
uses-material-design: true

example/flutter/objectbox_demo_desktop/pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ dependencies:
1111
flutter:
1212
sdk: flutter
1313
cupertino_icons: ^0.1.0
14-
objectbox: ^0.6.1
14+
objectbox: ^0.6.2
1515

1616
dev_dependencies:
1717
flutter_test:
1818
sdk: flutter
1919
build_runner: ^1.0.0
20-
objectbox_generator: ^0.6.1
20+
objectbox_generator: ^0.6.2
2121

2222
flutter:
2323
uses-material-design: true

generator/pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: objectbox_generator
2-
version: 0.6.1
2+
version: 0.6.2
33
repository: https://github.com/objectbox/objectbox-dart
44
homepage: https://objectbox.io
55
description: ObjectBox binding code generator - finds annotated entities and adds them to the ObjectBox DB model.
@@ -8,7 +8,7 @@ environment:
88
sdk: ">=2.5.0 <3.0.0"
99

1010
dependencies:
11-
objectbox: 0.6.1
11+
objectbox: 0.6.2
1212
build: ^1.0.0
1313
source_gen: ^0.9.0
1414
analyzer: ">=0.35.0 <0.100.0"

lib/src/bindings/bindings.dart

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class _ObjectBoxBindings {
1313
// common functions
1414
void Function(Pointer<Int32> major, Pointer<Int32> minor, Pointer<Int32> patch) obx_version;
1515
Pointer<Utf8> Function() obx_version_string;
16+
int Function() obx_supports_bytes_array;
1617

1718
obx_free_dart_t<OBX_bytes_array> obx_bytes_array_free;
1819
obx_free_dart_t<OBX_id_array> obx_id_array_free;
@@ -68,6 +69,10 @@ class _ObjectBoxBindings {
6869
int Function(Pointer<Void> box, int id, Pointer<Pointer<Uint8>> data, Pointer<IntPtr> size) obx_box_get;
6970
Pointer<OBX_bytes_array> Function(Pointer<Void> box, Pointer<OBX_id_array> ids) obx_box_get_many;
7071
Pointer<OBX_bytes_array> Function(Pointer<Void> box) obx_box_get_all;
72+
int Function(Pointer<Void> box, Pointer<OBX_id_array> ids, Pointer<NativeFunction<obx_data_visitor_native_t>> visitor,
73+
Pointer<Void> user_data) obx_box_visit_many;
74+
int Function(Pointer<Void> box, Pointer<NativeFunction<obx_data_visitor_native_t>> visitor, Pointer<Void> user_data)
75+
obx_box_visit_all;
7176
int Function(Pointer<Void> box, int id_or_zero) obx_box_id_for_put;
7277
int Function(Pointer<Void> box, int count, Pointer<Uint64> out_first_id) obx_box_ids_for_put;
7378
int Function(Pointer<Void> box, int id, Pointer<Uint8> data, int size, int mode) obx_box_put;
@@ -162,6 +167,7 @@ class _ObjectBoxBindings {
162167
// common functions
163168
obx_version = _fn<obx_version_native_t>("obx_version").asFunction();
164169
obx_version_string = _fn<obx_version_string_native_t>("obx_version_string").asFunction();
170+
obx_supports_bytes_array = _fn<obx_supports_bytes_array_native_t>("obx_supports_bytes_array").asFunction();
165171
obx_bytes_array_free = _fn<obx_free_native_t<Pointer<OBX_bytes_array>>>("obx_bytes_array_free").asFunction();
166172
obx_id_array_free = _fn<obx_free_native_t<Pointer<OBX_id_array>>>("obx_id_array_free").asFunction();
167173
// obx_string_array_free = _fn<obx_free_native_t<Pointer<>>>("obx_string_array_free").asFunction();
@@ -215,6 +221,8 @@ class _ObjectBoxBindings {
215221
obx_box_get = _fn<obx_box_get_native_t>("obx_box_get").asFunction();
216222
obx_box_get_many = _fn<obx_box_get_many_native_t>("obx_box_get_many").asFunction();
217223
obx_box_get_all = _fn<obx_box_get_all_native_t>("obx_box_get_all").asFunction();
224+
obx_box_visit_many = _fn<obx_box_visit_many_native_t>("obx_box_visit_many").asFunction();
225+
obx_box_visit_all = _fn<obx_box_visit_all_native_t>("obx_box_visit_all").asFunction();
218226
obx_box_id_for_put = _fn<obx_box_id_for_put_native_t>("obx_box_id_for_put").asFunction();
219227
obx_box_ids_for_put = _fn<obx_box_ids_for_put_native_t>("obx_box_ids_for_put").asFunction();
220228
obx_box_put = _fn<obx_box_put_native_t>("obx_box_put").asFunction();

lib/src/bindings/data_visitor.dart

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'dart:ffi';
2+
import 'signatures.dart';
3+
import "package:ffi/ffi.dart" show allocate, free;
4+
5+
/// This file implements C call forwarding using a trampoline approach.
6+
///
7+
/// When you want to pass a dart callback to a C function you cannot use lambdas and instead the callback must be
8+
/// a static function, otherwise `Pointer.fromFunction()` called with your function won't compile.
9+
/// Since static functions don't have any state, you must either rely on a global state or use a "userData" pointer
10+
/// pass-through functionality provided by a C function.
11+
///
12+
/// The DataVisitor class tries to alleviate the burden of managing this and instead allows using lambdas from
13+
/// user-code, internally mapping the C calls to the appropriate lambda.
14+
///
15+
/// Sample usage:
16+
/// final results = <T>[];
17+
/// final visitor = DataVisitor((Pointer<Uint8> dataPtr, int length) {
18+
/// final bytes = dataPtr.asTypedList(length);
19+
/// results.add(_fbManager.unmarshal(bytes));
20+
/// return true; // return value usually indicates to the C function whether it should continue.
21+
/// });
22+
///
23+
/// final err = bindings.obx_query_visit(_cQuery, visitor.fn, visitor.userData, offset, limit);
24+
/// visitor.close(); // make sure to close the visitor, unregistering the callback it from the forwarder
25+
/// checkObx(err);
26+
27+
int _lastId = 0;
28+
final _callbacks = <int, bool Function(Pointer<Uint8> dataPtr, int length)>{};
29+
30+
// called from C, forwards calls to the actual callback registered at the given ID
31+
int _forwarder(Pointer<Void> callbackId, Pointer<Uint8> dataPtr, int size) {
32+
if (callbackId == null || callbackId.address == 0) {
33+
throw Exception("Data-visitor callback issued with NULL user_data (callback ID)");
34+
}
35+
36+
return _callbacks[callbackId.cast<Int64>().value](dataPtr, size) ? 1 : 0;
37+
}
38+
39+
/// A data visitor wrapper/forwarder to be used where obx_data_visitor is expected.
40+
class DataVisitor {
41+
int _id;
42+
Pointer<Int64> _idPtr;
43+
44+
Pointer<NativeFunction<obx_data_visitor_native_t>> get fn => Pointer.fromFunction(_forwarder, 0);
45+
46+
Pointer<Void> get userData => _idPtr.cast<Void>();
47+
48+
DataVisitor(bool Function(Pointer<Uint8> dataPtr, int length) callback) {
49+
// cycle through ids until we find an empty slot
50+
_lastId++;
51+
var initialId = _lastId;
52+
while (_callbacks.containsKey(_lastId)) {
53+
_lastId++;
54+
55+
if (initialId == _lastId) {
56+
throw Exception("Data-visitor callbacks queue full - can't allocate another");
57+
}
58+
}
59+
// register the visitor
60+
_id = _lastId;
61+
_callbacks[_id] = callback;
62+
63+
_idPtr = allocate<Int64>();
64+
_idPtr.value = _id;
65+
}
66+
67+
void close() {
68+
// unregister the visitor
69+
_callbacks.remove(_id);
70+
free(_idPtr);
71+
}
72+
}

lib/src/bindings/signatures.dart

+12-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import 'package:ffi/ffi.dart';
77
// common functions
88
typedef obx_version_native_t = Void Function(Pointer<Int32> major, Pointer<Int32> minor, Pointer<Int32> patch);
99
typedef obx_version_string_native_t = Pointer<Utf8> Function();
10+
typedef obx_supports_bytes_array_native_t = Uint8 Function();
1011

1112
typedef obx_free_dart_t<T extends NativeType> = void Function(Pointer<T> ptr);
1213
typedef obx_free_native_t<T extends NativeType> = Void Function(T ptr); // no Pointer<T>, code analysis fails on usage
1314

15+
typedef obx_data_visitor_native_t = Uint8 Function(Pointer<Void> user_data, Pointer<Uint8> data, IntPtr size);
16+
1417
// error info
1518
typedef obx_last_error_code_native_t = Int32 Function();
1619
typedef obx_last_error_message_native_t = Pointer<Utf8> Function();
@@ -58,6 +61,10 @@ typedef obx_box_get_native_t = Int32 Function(
5861
Pointer<Void> box, Uint64 id, Pointer<Pointer<Uint8>> data, Pointer<IntPtr> size);
5962
typedef obx_box_get_many_native_t = Pointer<OBX_bytes_array> Function(Pointer<Void> box, Pointer<OBX_id_array> ids);
6063
typedef obx_box_get_all_native_t = Pointer<OBX_bytes_array> Function(Pointer<Void> box);
64+
typedef obx_box_visit_many_native_t = Int32 Function(Pointer<Void> box, Pointer<OBX_id_array> ids,
65+
Pointer<NativeFunction<obx_data_visitor_native_t>> visitor, Pointer<Void> user_data);
66+
typedef obx_box_visit_all_native_t = Int32 Function(
67+
Pointer<Void> box, Pointer<NativeFunction<obx_data_visitor_native_t>> visitor, Pointer<Void> user_data);
6168
typedef obx_box_id_for_put_native_t = Uint64 Function(Pointer<Void> box, Uint64 id_or_zero);
6269
typedef obx_box_ids_for_put_native_t = Int32 Function(Pointer<Void> box, Uint64 count, Pointer<Uint64> out_first_id);
6370
typedef obx_box_put_native_t = Int32 Function(
@@ -76,7 +83,7 @@ typedef obx_box_is_empty_native_t = Int32 Function(Pointer<Void> box, Pointer<Ui
7683
// typedef Pointer<Int8> -> char[]
7784
// typedef Pointer<Int32> -> int (e.g. obx_qb_cond);
7885

79-
// query builider
86+
// query builder
8087
typedef obx_query_builder_native_t = Pointer<Void> Function(Pointer<Void> store, Uint32 entity_id);
8188
typedef obx_query_builder_dart_t = Pointer<Void> Function(Pointer<Void> store, int entity_id);
8289

@@ -147,10 +154,10 @@ typedef obx_query_count_dart_t = int Function(Pointer<Void> query, Pointer<Uint6
147154

148155
typedef obx_query_describe_t = Pointer<Utf8> Function(Pointer<Void> query);
149156

150-
typedef obx_query_visit_native_t = Int32 Function(
151-
Pointer<Void> query, Pointer<Void> visitor, Pointer<Void> user_data, Uint64 offset, Uint64 limit);
152-
typedef obx_query_visit_dart_t = int Function(
153-
Pointer<Void> query, Pointer<Void> visitor, Pointer<Void> user_data, int offset, int limit);
157+
typedef obx_query_visit_native_t = Int32 Function(Pointer<Void> query,
158+
Pointer<NativeFunction<obx_data_visitor_native_t>> visitor, Pointer<Void> user_data, Uint64 offset, Uint64 limit);
159+
typedef obx_query_visit_dart_t = int Function(Pointer<Void> query,
160+
Pointer<NativeFunction<obx_data_visitor_native_t>> visitor, Pointer<Void> user_data, int offset, int limit);
154161

155162
// Utilities
156163

lib/src/box.dart

+43-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'common.dart';
55
import "store.dart";
66
import "bindings/bindings.dart";
77
import "bindings/constants.dart";
8+
import "bindings/data_visitor.dart";
89
import "bindings/flatbuffers.dart";
910
import "bindings/helpers.dart";
1011
import "bindings/structs.dart";
@@ -24,8 +25,9 @@ class Box<T> {
2425
ModelEntity _modelEntity;
2526
ObjectReader<T> _entityReader;
2627
OBXFlatbuffersManager<T> _fbManager;
28+
final bool _supportsBytesArrays;
2729

28-
Box(this._store) {
30+
Box(this._store) : _supportsBytesArrays = bindings.obx_supports_bytes_array() == 1 {
2931
EntityDefinition<T> entityDefs = _store.entityDef<T>();
3032
_modelEntity = entityDefs.model;
3133
_entityReader = entityDefs.reader;
@@ -160,34 +162,63 @@ class Box<T> {
160162
}
161163
}
162164

163-
List<T> _getMany(bool allowMissing, Pointer<OBX_bytes_array> Function() cCall) {
165+
List<T> _getMany(
166+
bool allowMissing, Pointer<OBX_bytes_array> Function() cGetArray, void Function(DataVisitor) cVisit) {
164167
return _store.runInTransaction(TxMode.Read, () {
165-
final bytesArray = cCall();
166-
try {
167-
return _fbManager.unmarshalArray(bytesArray, allowMissing: allowMissing);
168-
} finally {
169-
bindings.obx_bytes_array_free(bytesArray);
168+
if (_supportsBytesArrays) {
169+
final bytesArray = cGetArray();
170+
try {
171+
return _fbManager.unmarshalArray(bytesArray, allowMissing: allowMissing);
172+
} finally {
173+
bindings.obx_bytes_array_free(bytesArray);
174+
}
175+
} else {
176+
final results = <T>[];
177+
final visitor = DataVisitor((Pointer<Uint8> dataPtr, int length) {
178+
if (dataPtr == null || dataPtr.address == 0 || length == 0) {
179+
if (allowMissing) {
180+
results.add(null);
181+
return true;
182+
} else {
183+
throw Exception('Object not found');
184+
}
185+
}
186+
final bytes = dataPtr.asTypedList(length);
187+
results.add(_fbManager.unmarshal(bytes));
188+
return true;
189+
});
190+
191+
try {
192+
cVisit(visitor);
193+
} finally {
194+
visitor.close();
195+
}
196+
return results;
170197
}
171198
});
172199
}
173200

174201
/// Returns a list of [ids.length] Objects of type T, each corresponding to the location of its ID in [ids].
175-
/// Non-existant IDs become null.
202+
/// Non-existent IDs become null.
176203
List<T> getMany(List<int> ids) {
177204
if (ids.isEmpty) return [];
178205

179-
const bool allowMissing = true; // returns null if null is encountered in the data found
206+
const bool allowMissing = true; // result includes null if an object is missing
180207
return OBX_id_array.executeWith(
181208
ids,
182-
(ptr) => _getMany(allowMissing,
183-
() => checkObxPtr(bindings.obx_box_get_many(_cBox, ptr), "failed to get many objects from box")));
209+
(ptr) => _getMany(
210+
allowMissing,
211+
() => checkObxPtr(bindings.obx_box_get_many(_cBox, ptr), "failed to get many objects from box"),
212+
(DataVisitor visitor) => checkObx(bindings.obx_box_visit_many(_cBox, ptr, visitor.fn, visitor.userData))));
184213
}
185214

186215
/// Returns all stored objects in this Box.
187216
List<T> getAll() {
188217
const bool allowMissing = false; // throw if null is encountered in the data found
189218
return _getMany(
190-
allowMissing, () => checkObxPtr(bindings.obx_box_get_all(_cBox), "failed to get all objects from box"));
219+
allowMissing,
220+
() => checkObxPtr(bindings.obx_box_get_all(_cBox), "failed to get all objects from box"),
221+
(DataVisitor visitor) => checkObx(bindings.obx_box_visit_all(_cBox, visitor.fn, visitor.userData)));
191222
}
192223

193224
/// Returns a builder to create queries for Object matching supplied criteria.

lib/src/query/query.dart

+20-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import "../store.dart";
77
import "../common.dart";
88
import "../bindings/bindings.dart";
99
import "../bindings/constants.dart";
10+
import "../bindings/data_visitor.dart";
1011
import "../bindings/flatbuffers.dart";
1112
import "../bindings/helpers.dart";
1213
import "../bindings/structs.dart";
@@ -571,11 +572,25 @@ class Query<T> {
571572

572573
List<T> find({int offset = 0, int limit = 0}) {
573574
return _store.runInTransaction(TxMode.Read, () {
574-
final bytesArray = checkObxPtr(bindings.obx_query_find(_cQuery, offset, limit), "find");
575-
try {
576-
return _fbManager.unmarshalArray(bytesArray);
577-
} finally {
578-
bindings.obx_bytes_array_free(bytesArray);
575+
if (bindings.obx_supports_bytes_array() == 1) {
576+
final bytesArray = checkObxPtr(bindings.obx_query_find(_cQuery, offset, limit), "find");
577+
try {
578+
return _fbManager.unmarshalArray(bytesArray);
579+
} finally {
580+
bindings.obx_bytes_array_free(bytesArray);
581+
}
582+
} else {
583+
final results = <T>[];
584+
final visitor = DataVisitor((Pointer<Uint8> dataPtr, int length) {
585+
final bytes = dataPtr.asTypedList(length);
586+
results.add(_fbManager.unmarshal(bytes));
587+
return true;
588+
});
589+
590+
final err = bindings.obx_query_visit(_cQuery, visitor.fn, visitor.userData, offset, limit);
591+
visitor.close();
592+
checkObx(err);
593+
return results;
579594
}
580595
});
581596
}

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: objectbox
2-
version: 0.6.1
2+
version: 0.6.2
33
repository: https://github.com/objectbox/objectbox-dart
44
homepage: https://objectbox.io
55
description: ObjectBox is a super-fast NoSQL ACID compliant object database.

test/box_test.dart

+23-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,29 @@ void main() {
7878
}
7979
});
8080

81-
test(".getMany correctly handles non-existant items", () {
81+
test(".getAll/getMany works on large arrays", () {
82+
// This would fail on 32-bit system if objectbox-c obx_supports_bytes_array() wasn't respected
83+
final length = 10 * 1000;
84+
final largeString = 'A' * length;
85+
expect(largeString.length, length);
86+
87+
box.put(TestEntity(tString: largeString));
88+
box.put(TestEntity(tString: largeString));
89+
90+
List<TestEntity> items = box.getAll();
91+
expect(items.length, 2);
92+
expect(items[0].tString, largeString);
93+
expect(items[1].tString, largeString);
94+
95+
box.put(TestEntity(tString: largeString));
96+
97+
items = box.getMany([1, 2]);
98+
expect(items.length, 2);
99+
expect(items[0].tString, largeString);
100+
expect(items[1].tString, largeString);
101+
});
102+
103+
test(".getMany correctly handles non-existent items", () {
82104
final List<TestEntity> items = ["One", "Two"].map((s) => TestEntity(tString: s)).toList();
83105
final List<int> ids = box.putMany(items);
84106
int otherId = 1;

0 commit comments

Comments
 (0)