diff --git a/.gitignore b/.gitignore index 1da856a3e..ac981d876 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ lib/*.so lib/*.a .vscode/ **/*.g.dart +doc/api diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e2677e17..cff89d890 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,21 @@ +0.5.0 (2019-11-18) +------------------ +* Dart 2.6 support - breaking change due to Dart 2.6 FFI changes. + Please keep using 0.4 if you're on Dart 2.5 or Flutter. Currently no Flutter version comes with Dart 2.6 final. + (thanks [Jasm Sison](https://github.com/Buggaboo) for [#54](https://github.com/objectbox/objectbox-dart/pull/57)) +* Docs fixes & improvements + 0.4.0 (2019-10-31) ------------------ * Flutter Android support * Queries for all currently supported types - (thanks [Jasm Sison](https://github.com/Buggaboo) for [#27](https://github.com/objectbox/objectbox-dart/pull/27) and [#46](https://github.com/objectbox/objectbox-dart/pull/46)] + (thanks [Jasm Sison](https://github.com/Buggaboo) for [#27](https://github.com/objectbox/objectbox-dart/pull/27) and [#46](https://github.com/objectbox/objectbox-dart/pull/46)) * More Box functions (count, isEmpty, contains, remove and their bulk variants) - (thanks [liquidiert](https://github.com/liquidiert) for [#42](https://github.com/objectbox/objectbox-dart/pull/42) and [#45](https://github.com/objectbox/objectbox-dart/pull/45)] + (thanks [liquidiert](https://github.com/liquidiert) for [#42](https://github.com/objectbox/objectbox-dart/pull/42) and [#45](https://github.com/objectbox/objectbox-dart/pull/45)) * Explicit write transactions - (thanks [liquidiert](https://github.com/liquidiert) for [#50](https://github.com/objectbox/objectbox-dart/pull/50)] + (thanks [liquidiert](https://github.com/liquidiert) for [#50](https://github.com/objectbox/objectbox-dart/pull/50)) * Resolved linter issues - (thanks [Gregory Sech](https://github.com/GregorySech) for [#31](https://github.com/objectbox/objectbox-dart/pull/31)] + (thanks [Gregory Sech](https://github.com/GregorySech) for [#31](https://github.com/objectbox/objectbox-dart/pull/31)) * Updated to objectbox-c 0.7.2 * First release on pub.dev @@ -17,12 +24,12 @@ * ID/UID generation and model persistence (objectbox-model.json) * CI tests using GitHub Actions * Code cleanup, refactoring and formatting - (thanks [Jasm Sison](https://github.com/Buggaboo) for [#20](https://github.com/objectbox/objectbox-dart/pull/20) & [#21](https://github.com/objectbox/objectbox-dart/pull/21)] + (thanks [Jasm Sison](https://github.com/Buggaboo) for [#20](https://github.com/objectbox/objectbox-dart/pull/20) & [#21](https://github.com/objectbox/objectbox-dart/pull/21)) 0.2.0 (2019-09-11) -------------------Buggaboo +------------------ * UTF-8 support for Store and Box - (thanks to [Jasm Sison](https://github.com/Buggaboo) for [#14](https://github.com/objectbox/objectbox-dart/pull/14)!) + (thanks [Jasm Sison](https://github.com/Buggaboo) for [#14](https://github.com/objectbox/objectbox-dart/pull/14)!) * Bulk put and get functions (getMany, getAll, putMany) * Updated to objectbox-c 0.7 * Basic Store options diff --git a/README.md b/README.md index c24f9798a..aec5b953c 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,27 @@ ObjectBox for Dart/Flutter ========================== ObjectBox for Dart is a standalone database storing Dart objects locally, with strong ACID semantics. +Flutter/Dart compatibility +-------------------------- +This library depends on a new Dart feature, FFI, introduced in Dart 2.5 (Flutter 1.9) as a feature preview. +However, it has been change significantly significantly in Dart 2.6 (future Flutter 1.10.x), i.e. introduced breaking changes we had to reflect. +Versions starting with ObjectBox 0.5 support Dart 2.6 as well as Flutter 1.10 (when it's finally released). + +The last supported version for Flutter 1.9/Dart 2.5 is ObjectBox 0.4.*, so if you can't upgrade yet, please use latest 0.4.x version instead. +For Flutter users, this is the only option, as long as a new version of Flutter (1.10), including Dart 2.6 is released. + +If you're developing standalone/non-flutter dart programs, you can already use Dart 2.6 with the latest ObjectBox version. + Installation ------------ Add the following dependencies to your `pubspec.yaml`: ```yaml dependencies: - objectbox: ^0.4.0 + objectbox: ^0.5.0 dev_dependencies: build_runner: ^1.0.0 - objectbox_generator: ^0.4.0 + objectbox_generator: ^0.5.0 ``` Proceed based on whether you're developing a Flutter app or a standalone dart program: @@ -34,7 +45,7 @@ Proceed based on whether you're developing a Flutter app or a standalone dart pr * macOS: if dart later complains that it cannot find the `libobjectbox.dylib` you probably have to unsign the `dart` binary (source: [dart issue](https://github.com/dart-lang/sdk/issues/38314#issuecomment-534102841)): ```shell script - sudo xcode --remove-signature $(which dart) + sudo codesign --remove-signature $(which dart) ``` * Windows: use "Git Bash" or similar to execute the following command ```shell script @@ -111,17 +122,17 @@ queryNullText.close(); // We have to manually close queries and query builders. ``` More complex queries can be constructed using `and/or` operators. -Also there is basic operator overloading support for `equal`, `greater`, `less`, `and` and `or`, -respectively `==`, `>`, `<`, `&`, `|`. +Also there is basic operator overloading support for `greater`, `less`, `and` and `or`, +respectively `>`, `<`, `&`, `|`. ```dart // final box ... -box.query(value.greaterThan(10).or(date.IsNull())).build(); +box.query(value.greaterThan(10).or(date.isNull())).build(); // equivalent to -final overloaded = (value > 10) | date.IsNull(); +final overloaded = (value > 10) | date.isNull(); box.query(overloaded as Condition).build(); // the cast is necessary due to the type analyzer ``` @@ -137,7 +148,7 @@ final q = box.query(Entity_.number > 0) // ... final qt = box.query(Entity_.text.notNull()) - .order(Entity_.text, flags: OBXOrderFlag.DESCENDING | OBXOrderFlag.CASE_SENSITIVE) + .order(Entity_.text, flags: Order.descending | Order.caseSensitive) .build(); ``` diff --git a/generator/pubspec.yaml b/generator/pubspec.yaml index 0fb264ead..491e2e71a 100644 --- a/generator/pubspec.yaml +++ b/generator/pubspec.yaml @@ -1,5 +1,5 @@ name: objectbox_generator -version: 0.4.0 +version: 0.5.0 repository: https://github.com/objectbox/objectbox-dart homepage: https://objectbox.io author: objectbox.io @@ -9,7 +9,7 @@ environment: sdk: ">=2.5.0 <3.0.0" dependencies: - objectbox: 0.4.0 + objectbox: 0.5.0 build: ^1.0.0 source_gen: ^0.9.0 analyzer: ">=0.35.0 <0.100.0" diff --git a/lib/objectbox.dart b/lib/objectbox.dart index 2e4837cfc..7285a5013 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -1,8 +1,5 @@ library objectbox; -export "dart:io"; // needed for generated files -export "dart:convert"; - export "src/annotations.dart"; export "src/common.dart"; export "src/model.dart"; diff --git a/lib/src/bindings/bindings.dart b/lib/src/bindings/bindings.dart index a0eb2e37b..8a12a9c16 100644 --- a/lib/src/bindings/bindings.dart +++ b/lib/src/bindings/bindings.dart @@ -13,15 +13,16 @@ class _ObjectBoxBindings { // common functions void Function(Pointer major, Pointer minor, Pointer patch) obx_version; Pointer Function() obx_version_string; - void Function(Pointer structPtr) obx_string_array_free, - obx_int64_array_free, - obx_int32_array_free, - obx_int16_array_free, - obx_int8_array_free, - obx_double_array_free, - obx_float_array_free; - obx_free_t obx_bytes_array_free; - obx_free_t obx_id_array_free; + + obx_free_dart_t obx_bytes_array_free; + obx_free_dart_t obx_id_array_free; + // obx_free_dart_t obx_string_array_free; + // obx_free_dart_t obx_int64_array_free; + // obx_free_dart_t obx_int32_array_free; + // obx_free_dart_t obx_int16_array_free; + // obx_free_dart_t obx_int8_array_free; + // obx_free_dart_t obx_double_array_free; + // obx_free_dart_t obx_float_array_free; // error info int Function() obx_last_error_code; @@ -154,15 +155,15 @@ class _ObjectBoxBindings { // common functions obx_version = _fn("obx_version").asFunction(); obx_version_string = _fn("obx_version_string").asFunction(); - obx_bytes_array_free = _fn>("obx_bytes_array_free").asFunction(); - obx_id_array_free = _fn>("obx_id_array_free").asFunction(); - obx_string_array_free = _fn("obx_string_array_free").asFunction(); - obx_int64_array_free = _fn("obx_int64_array_free").asFunction(); - obx_int32_array_free = _fn("obx_int32_array_free").asFunction(); - obx_int16_array_free = _fn("obx_int16_array_free").asFunction(); - obx_int8_array_free = _fn("obx_int8_array_free").asFunction(); - obx_double_array_free = _fn("obx_double_array_free").asFunction(); - obx_float_array_free = _fn("obx_float_array_free").asFunction(); + obx_bytes_array_free = _fn>>("obx_bytes_array_free").asFunction(); + obx_id_array_free = _fn>>("obx_id_array_free").asFunction(); +// obx_string_array_free = _fn>>("obx_string_array_free").asFunction(); +// obx_int64_array_free = _fn>>("obx_int64_array_free").asFunction(); +// obx_int32_array_free = _fn>>("obx_int32_array_free").asFunction(); +// obx_int16_array_free = _fn>>("obx_int16_array_free").asFunction(); +// obx_int8_array_free = _fn>>("obx_int8_array_free").asFunction(); +// obx_double_array_free = _fn>>("obx_double_array_free").asFunction(); +// obx_float_array_free = _fn>>("obx_float_array_free").asFunction(); // error info obx_last_error_code = _fn("obx_last_error_code").asFunction(); diff --git a/lib/src/bindings/flatbuffers.dart b/lib/src/bindings/flatbuffers.dart index 322fb3a73..b080be954 100644 --- a/lib/src/bindings/flatbuffers.dart +++ b/lib/src/bindings/flatbuffers.dart @@ -136,7 +136,7 @@ class OBXFlatbuffersManager { // expects pointer to OBX_bytes_array and manually resolves its contents (see objectbox.h) List unmarshalArray(final Pointer bytesArray, {bool allowMissing = false}) { - final OBX_bytes_array array = bytesArray.load(); + final OBX_bytes_array array = bytesArray.ref; var fn = (OBX_bytes b) => unmarshal(b.data); if (allowMissing) { fn = (OBX_bytes b) => b.isEmpty ? null : unmarshal(b.data); diff --git a/lib/src/bindings/signatures.dart b/lib/src/bindings/signatures.dart index 1da20d4d8..41315b7d5 100644 --- a/lib/src/bindings/signatures.dart +++ b/lib/src/bindings/signatures.dart @@ -6,8 +6,9 @@ import 'structs.dart'; // common functions typedef obx_version_native_t = Void Function(Pointer major, Pointer minor, Pointer patch); typedef obx_version_string_native_t = Pointer Function(); -typedef obx_free_t = Void Function(Pointer ptr); -typedef obx_free_struct_native_t = Void Function(Pointer structPtr); + +typedef obx_free_dart_t = void Function(Pointer ptr); +typedef obx_free_native_t = Void Function(T ptr); // no Pointer, code analysis fails on usage // error info typedef obx_last_error_code_native_t = Int32 Function(); diff --git a/lib/src/bindings/structs.dart b/lib/src/bindings/structs.dart index 21a56bb1f..7421de0a6 100644 --- a/lib/src/bindings/structs.dart +++ b/lib/src/bindings/structs.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; -import "dart:typed_data" show Uint8List, Uint64List; +import "dart:typed_data" show Uint8List; +import "package:ffi/ffi.dart" show allocate, free; import '../common.dart'; @@ -10,34 +11,34 @@ import '../common.dart'; /// obx_id* ids; /// size_t count; /// }; -class OBX_id_array extends Struct { +class OBX_id_array extends Struct { Pointer _itemsPtr; @IntPtr() // size_t int length; /// Get a copy of the list - List items() => Uint64List.view(_itemsPtr.asExternalTypedData(count: length).buffer).toList(); + List items() => _itemsPtr.asTypedList(length); /// Execute the given function, managing the resources consistently static R executeWith(List items, R Function(Pointer) fn) { // allocate a temporary structure - final ptr = Pointer.allocate(); + final ptr = allocate(); // fill it with data - OBX_id_array array = ptr.load(); + OBX_id_array array = ptr.ref; array.length = items.length; - array._itemsPtr = Pointer.allocate(count: array.length); + array._itemsPtr = allocate(count: array.length); for (int i = 0; i < items.length; ++i) { - array._itemsPtr.elementAt(i).store(items[i]); + array._itemsPtr.elementAt(i).value = items[i]; } // call the function with the structure and free afterwards try { return fn(ptr); } finally { - array._itemsPtr.free(); - ptr.free(); + free(array._itemsPtr); + free(ptr); } } } @@ -47,16 +48,15 @@ class OBX_id_array extends Struct { /// const void* data; /// size_t size; /// }; -class OBX_bytes extends Struct { +class OBX_bytes extends Struct { Pointer _dataPtr; @IntPtr() // size_t int length; /// Get access to the data (no-copy) - Uint8List get data => isEmpty - ? throw ObjectBoxException("can't access data of empty OBX_bytes") - : Uint8List.view(_dataPtr.asExternalTypedData(count: length).buffer); + Uint8List get data => + isEmpty ? throw ObjectBoxException("can't access data of empty OBX_bytes") : _dataPtr.asTypedList(length); bool get isEmpty => length == 0 || _dataPtr.address == 0; @@ -65,8 +65,8 @@ class OBX_bytes extends Struct { /// Returns a pointer to OBX_bytes with copy of the passed data. /// Warning: this creates an two unmanaged pointers which must be freed manually: OBX_bytes.freeManaged(result). static Pointer managedCopyOf(Uint8List data) { - final ptr = Pointer.allocate(); - final OBX_bytes bytes = ptr.load(); + final ptr = allocate(); + final OBX_bytes bytes = ptr.ref; const align = true; // ObjectBox requires data to be aligned to the length of 4 bytes.length = align ? ((data.length + 3.0) ~/ 4.0) * 4 : data.length; @@ -79,9 +79,9 @@ class OBX_bytes extends Struct { // } // create a copy of the data - bytes._dataPtr = Pointer.allocate(count: bytes.length); + bytes._dataPtr = allocate(count: bytes.length); for (int i = 0; i < data.length; ++i) { - bytes._dataPtr.elementAt(i).store(data[i]); + bytes._dataPtr.elementAt(i).value = data[i]; } return ptr; @@ -89,9 +89,9 @@ class OBX_bytes extends Struct { /// Free a dart-created OBX_bytes pointer. static void freeManaged(Pointer ptr) { - final OBX_bytes bytes = ptr.load(); - bytes._dataPtr.free(); - ptr.free(); + final OBX_bytes bytes = ptr.ref; + free(bytes._dataPtr); + free(ptr); } } @@ -100,7 +100,7 @@ class OBX_bytes extends Struct { /// OBX_bytes* bytes; /// size_t count; /// }; -class OBX_bytes_array extends Struct { +class OBX_bytes_array extends Struct { Pointer _items; @IntPtr() // size_t @@ -110,51 +110,8 @@ class OBX_bytes_array extends Struct { List items() { final result = List(); for (int i = 0; i < length; i++) { - result.add(_items.elementAt(i).load()); + result.add(_items.elementAt(i).ref); } return result; } - - /// TODO: try this with new Dart 2.6 FFI... with the previous versions it was causing memory corruption issues. - /// It's supposed to be used by PutMany() -// /// Create a dart-managed OBX_bytes_array. -// static Pointer createManaged(int count) { -// final ptr = Pointer.allocate(); -// final OBX_bytes_array array = ptr.load(); -// array.length = count; -// array._items = Pointer.allocate(count: count); -// return ptr; -// } -// -// /// Replace the data at the given index with the passed pointer. -// void setAndFree(int i, Pointer src) { -// assert(i >= 0 && i < length); -// -// final OBX_bytes srcBytes = src.load(); -// final OBX_bytes tarBytes = _items.elementAt(i).load(); -// -// assert(!srcBytes.isEmpty); -// assert(tarBytes.isEmpty); -// -// tarBytes._dataPtr = srcBytes._dataPtr; -// tarBytes.length = srcBytes.length; -// -// srcBytes._dataPtr.store(nullptr.address); -// srcBytes.length = 0; -// src.free(); -// } -// -// /// Free a dart-created OBX_bytes pointer. -// static void freeManaged(Pointer ptr, bool freeIncludedBytes) { -// final OBX_bytes_array array = ptr.load(); -// if (freeIncludedBytes) { -// for (int i = 0; i < array.length; i++) { -// // Calling OBX_bytes.freeManaged() would cause double free -// final OBX_bytes bytes = array._items.elementAt(i).load(); -// bytes._dataPtr.free(); -// } -// } -// array._items.free(); -// ptr.free(); -// } } diff --git a/lib/src/box.dart b/lib/src/box.dart index 3b77e2fde..eda7fa581 100644 --- a/lib/src/box.dart +++ b/lib/src/box.dart @@ -1,5 +1,5 @@ import "dart:ffi"; -import 'dart:typed_data'; +import "package:ffi/ffi.dart" show allocate, free; import 'common.dart'; import "store.dart"; @@ -17,6 +17,7 @@ enum _PutMode { Update, } +/// A box to store objects of a particular class. class Box { Store _store; Pointer _cBox; @@ -45,18 +46,23 @@ class Box { } } - // if the respective ID property is given as null or 0, a newly assigned ID is returned, otherwise the existing ID is returned + /// Puts the given Object in the box (aka persisting it). If this is a new entity (its ID property is 0), a new ID + /// will be assigned to the entity (and returned). If the entity was already put in the box before, it will be + /// overwritten. + /// + /// Performance note: if you want to put several entities, consider [putMany] instead. int put(T object, {_PutMode mode = _PutMode.Put}) { var propVals = _entityReader(object); if (propVals[_modelEntity.idPropName] == null || propVals[_modelEntity.idPropName] == 0) { - final id = bindings.obx_box_id_for_put(_cBox, 0); // TODO check error if 0 was returned instead of an ID + final id = bindings.obx_box_id_for_put(_cBox, 0); + if (id == 0) throw ObjectBoxException(lastObxErrorString()); propVals[_modelEntity.idPropName] = id; } // put object into box and free the buffer final Pointer bytesPtr = _fbManager.marshal(propVals); try { - final OBX_bytes bytes = bytesPtr.load(); + final OBX_bytes bytes = bytesPtr.ref; checkObx(bindings.obx_box_put( _cBox, propVals[_modelEntity.idPropName], bytes.ptr, bytes.length, _getOBXPutMode(mode))); } finally { @@ -66,7 +72,8 @@ class Box { return propVals[_modelEntity.idPropName]; } - // only instances whose ID property ot null or 0 will be given a new, valid number for that. A list of the final IDs is returned + /// Puts the given [objects] into this Box in a single transaction. Returns a list of all IDs of the inserted + /// Objects. List putMany(List objects, {_PutMode mode = _PutMode.Put}) { if (objects.isEmpty) return []; @@ -82,12 +89,12 @@ class Box { // generate new IDs for these instances and set them if (missingIdsCount != 0) { int nextId = 0; - Pointer nextIdPtr = Pointer.allocate(count: 1); + Pointer nextIdPtr = allocate(count: 1); try { checkObx(bindings.obx_box_ids_for_put(_cBox, missingIdsCount, nextIdPtr)); - nextId = nextIdPtr.load(); + nextId = nextIdPtr.value; } finally { - nextIdPtr.free(); + free(nextIdPtr); } for (var instPropVals in allPropVals) { if (instPropVals[_modelEntity.idPropName] == null || instPropVals[_modelEntity.idPropName] == 0) { @@ -98,60 +105,57 @@ class Box { // because obx_box_put_many also needs a list of all IDs of the elements to be put into the box, // generate this list now (only needed if not all IDs have been generated) - Pointer allIdsMemory = Pointer.allocate(count: objects.length); + Pointer allIdsMemory = allocate(count: objects.length); try { for (int i = 0; i < allPropVals.length; ++i) { - allIdsMemory.elementAt(i).store(allPropVals[i][_modelEntity.idPropName] as int); + allIdsMemory.elementAt(i).value = (allPropVals[i][_modelEntity.idPropName] as int); } // marshal all objects to be put into the box - // final bytesArrayPtr = OBX_bytes_array.createManaged(allPropVals.length); final bytesArrayPtr = checkObxPtr(bindings.obx_bytes_array(allPropVals.length), "could not create OBX_bytes_array"); final listToFree = List>(); try { - // final OBX_bytes_array bytesArray = bytesArrayPtr.load(); for (int i = 0; i < allPropVals.length; i++) { - // bytesArray.setAndFree(i, _fbManager.marshal(allPropVals[i])); final bytesPtr = _fbManager.marshal(allPropVals[i]); listToFree.add(bytesPtr); - final OBX_bytes bytes = bytesPtr.load(); + final OBX_bytes bytes = bytesPtr.ref; bindings.obx_bytes_array_set(bytesArrayPtr, i, bytes.ptr, bytes.length); } checkObx(bindings.obx_box_put_many(_cBox, bytesArrayPtr, allIdsMemory, _getOBXPutMode(mode))); } finally { - // OBX_bytes_array.freeManaged(bytesArrayPtr, true); bindings.obx_bytes_array_free(bytesArrayPtr); listToFree.forEach(OBX_bytes.freeManaged); } } finally { - allIdsMemory.free(); + free(allIdsMemory); } return allPropVals.map((p) => p[_modelEntity.idPropName] as int).toList(); } + /// Retrieves the stored object with the ID [id] from this box's database. Returns null if not found. get(int id) { - final dataPtrPtr = Pointer>.allocate(); - final sizePtr = Pointer.allocate(); + final dataPtrPtr = allocate>(); + final sizePtr = allocate(); try { // get element with specified id from database return _store.runInTransaction(TxMode.Read, () { checkObx(bindings.obx_box_get(_cBox, id, dataPtrPtr, sizePtr)); - Pointer dataPtr = dataPtrPtr.load(); - final size = sizePtr.load(); + Pointer dataPtr = dataPtrPtr.value; + final size = sizePtr.value; // create a no-copy view - final bytes = Uint8List.view(dataPtr.asExternalTypedData(count: size).buffer); + final bytes = dataPtr.asTypedList(size); return _fbManager.unmarshal(bytes); }); } finally { - dataPtrPtr.free(); - sizePtr.free(); + free(dataPtrPtr); + free(sizePtr); } } @@ -166,7 +170,8 @@ class Box { }); } - // returns list of ids.length objects of type T, each corresponding to the location of its ID in the ids array. Non-existant IDs become null + /// Returns a list of [ids.length] Objects of type T, each corresponding to the location of its ID in [ids]. + /// Non-existant IDs become null. List getMany(List ids) { if (ids.isEmpty) return []; @@ -177,56 +182,65 @@ class Box { () => checkObxPtr(bindings.obx_box_get_many(_cBox, ptr), "failed to get many objects from box"))); } + /// Returns all stored objects in this Box. List getAll() { const bool allowMissing = false; // throw if null is encountered in the data found return _getMany( allowMissing, () => checkObxPtr(bindings.obx_box_get_all(_cBox), "failed to get all objects from box")); } + /// Returns a builder to create queries for Object matching supplied criteria. QueryBuilder query(Condition qc) => QueryBuilder(_store, _fbManager, _modelEntity.id.id, qc); + /// Returns the count of all stored Objects in this box or, if [limit] is not zero, the given [limit], whichever + /// is lower. int count({int limit = 0}) { - Pointer count = Pointer.allocate(); + Pointer count = allocate(); try { checkObx(bindings.obx_box_count(_cBox, limit, count)); - return count.load(); + return count.value; } finally { - count.free(); + free(count); } } + /// Returns true if no objects are in this box. bool isEmpty() { - Pointer isEmpty = Pointer.allocate(); + Pointer isEmpty = allocate(); try { checkObx(bindings.obx_box_is_empty(_cBox, isEmpty)); - return isEmpty.load() > 0 ? true : false; + return isEmpty.value > 0 ? true : false; } finally { - isEmpty.free(); + free(isEmpty); } } + /// Returns true if this box contains an Object with the ID [id]. bool contains(int id) { - Pointer contains = Pointer.allocate(); + Pointer contains = allocate(); try { checkObx(bindings.obx_box_contains(_cBox, id, contains)); - return contains.load() > 0 ? true : false; + return contains.value > 0 ? true : false; } finally { - contains.free(); + free(contains); } } + /// Returns true if this box contains objects with all of the given [ids] using a single transaction. bool containsMany(List ids) { - Pointer contains = Pointer.allocate(); + Pointer contains = allocate(); try { return OBX_id_array.executeWith(ids, (ptr) { checkObx(bindings.obx_box_contains_many(_cBox, ptr, contains)); - return contains.load() > 0 ? true : false; + return contains.value > 0 ? true : false; }); } finally { - contains.free(); + free(contains); } } + /// Removes (deletes) the Object with the ID [id]. Returns true if an entity was actually removed and false if no + /// entity exists with the given ID. bool remove(int id) { final err = bindings.obx_box_remove(_cBox, id); if (err == OBXError.OBX_NOT_FOUND) return false; @@ -234,27 +248,30 @@ class Box { return true; } + /// Removes (deletes) Objects by their ID in a single transaction. Returns a list of IDs of all removed Objects. int removeMany(List ids) { - Pointer removedIds = Pointer.allocate(); + Pointer removedIds = allocate(); try { return OBX_id_array.executeWith(ids, (ptr) { checkObx(bindings.obx_box_remove_many(_cBox, ptr, removedIds)); - return removedIds.load(); + return removedIds.value; }); } finally { - removedIds.free(); + free(removedIds); } } + /// Removes (deletes) ALL Objects in a single transaction. int removeAll() { - Pointer removedItems = Pointer.allocate(); + Pointer removedItems = allocate(); try { checkObx(bindings.obx_box_remove_all(_cBox, removedItems)); - return removedItems.load(); + return removedItems.value; } finally { - removedItems.free(); + free(removedItems); } } + /// The low-level pointer to this box. get ptr => _cBox; } diff --git a/lib/src/common.dart b/lib/src/common.dart index 84bec8479..fdc1bc248 100644 --- a/lib/src/common.dart +++ b/lib/src/common.dart @@ -1,4 +1,5 @@ import "dart:ffi"; +import "package:ffi/ffi.dart" show allocate, free; import "bindings/bindings.dart"; @@ -14,15 +15,15 @@ class Version { /// Returns the underlying ObjectBox-C library version Version versionLib() { - var majorPtr = Pointer.allocate(), minorPtr = Pointer.allocate(), patchPtr = Pointer.allocate(); + var majorPtr = allocate(), minorPtr = allocate(), patchPtr = allocate(); try { bindings.obx_version(majorPtr, minorPtr, patchPtr); - return Version(majorPtr.load(), minorPtr.load(), patchPtr.load()); + return Version(majorPtr.value, minorPtr.value, patchPtr.value); } finally { - majorPtr.free(); - minorPtr.free(); - patchPtr.free(); + free(majorPtr); + free(minorPtr); + free(patchPtr); } } diff --git a/lib/src/model.dart b/lib/src/model.dart index 83e3a4794..c5786c77c 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -47,7 +47,7 @@ class Model { try { _check(bindings.obx_model_entity(_cModel, name.cast(), entity.id.id, entity.id.uid)); } finally { - name.free(); + free(name); } // add all properties @@ -62,7 +62,7 @@ class Model { try { _check(bindings.obx_model_property(_cModel, name.cast(), prop.type, prop.id.id, prop.id.uid)); } finally { - name.free(); + free(name); } if (prop.flags != 0) { diff --git a/lib/src/query/query.dart b/lib/src/query/query.dart index 2a7e1d7be..25f151566 100644 --- a/lib/src/query/query.dart +++ b/lib/src/query/query.dart @@ -2,6 +2,8 @@ library query; import "dart:ffi"; +import "package:ffi/ffi.dart" show allocate, free, Utf8; + import "../store.dart"; import "../common.dart"; import "../bindings/bindings.dart"; @@ -10,7 +12,6 @@ import "../bindings/flatbuffers.dart"; import "../bindings/helpers.dart"; import "../bindings/structs.dart"; import "../bindings/signatures.dart"; -import "package:ffi/ffi.dart"; part "builder.dart"; @@ -33,7 +34,6 @@ class Order { static final nullsAsZero = 16; } - /// The QueryProperty types are responsible for the operator overloading. /// A QueryBuilder will be constructed, based on the any / all operations applied. /// When build() is called on the QueryBuilder a Query object will be created. @@ -295,40 +295,41 @@ class StringCondition extends PropertyCondition { int _op1(QueryBuilder builder, obx_qb_cond_string_op_1_dart_t func) { final utf8Str = Utf8.toUtf8(_value); + var uint8Str = utf8Str.cast(); try { - var uint8Str = utf8Str.cast(); return func(builder._cBuilder, _property._propertyId, uint8Str, _caseSensitive ? 1 : 0); } finally { - utf8Str.free(); + // https://github.com/dart-lang/ffi/blob/master/lib/src/utf8.dart#L56 + free(utf8Str); } } int _inside(QueryBuilder builder) { final func = bindings.obx_qb_string_in; final listLength = _list.length; - final arrayOfUint8Ptrs = Pointer>.allocate(count: listLength); + final arrayOfUint8Ptrs = allocate>(count: listLength); try { for (int i = 0; i < _list.length; i++) { var uint8Str = Utf8.toUtf8(_list[i]).cast(); - arrayOfUint8Ptrs.elementAt(i).store(uint8Str); + arrayOfUint8Ptrs.elementAt(i).value = uint8Str; } return func(builder._cBuilder, _property._propertyId, arrayOfUint8Ptrs, listLength, _caseSensitive ? 1 : 0); } finally { for (int i = 0; i < _list.length; i++) { - var uint8Str = arrayOfUint8Ptrs.elementAt(i).load(); - uint8Str.free(); // I assume the casted Uint8 retains the same Utf8 address + var uint8Str = arrayOfUint8Ptrs.elementAt(i).value; + free(uint8Str); // I assume the casted Uint8 retains the same Utf8 address } - arrayOfUint8Ptrs.free(); // It probably doesn't release recursively + free(arrayOfUint8Ptrs); // It probably doesn't release recursively } } int _opWithEqual(QueryBuilder builder, obx_qb_string_lt_gt_op_dart_t func) { final utf8Str = Utf8.toUtf8(_value); + var uint8Str = utf8Str.cast(); try { - var uint8Str = utf8Str.cast(); return func(builder._cBuilder, _property._propertyId, uint8Str, _caseSensitive ? 1 : 0, _withEqual ? 1 : 0); } finally { - utf8Str.free(); + free(utf8Str); } } @@ -370,19 +371,19 @@ class IntegerCondition extends PropertyCondition { return func(builder._cBuilder, _property._propertyId, _value); } - // ideally it should be implemented like this, but this doesn't work, TODO report to google + // ideally it should be implemented like this, but this doesn't work, TODO report to google, doesn't work with 2.6 yet /* int _opList

(QueryBuilder builder, obx_qb_cond_operator_in_dart_t

func) { int length = _list.length; - final listPtr = Pointer

.allocate(count: length); + final listPtr = allocate

(count: length); try { for (int i=0; i { // TODO replace nasty duplication with implementation above, when fix is in int _opList32(QueryBuilder builder, obx_qb_cond_operator_in_dart_t func) { int length = _list.length; - final listPtr = Pointer.allocate(count: length); + final listPtr = allocate(count: length); try { for (int i = 0; i < length; i++) { - listPtr.elementAt(i).store(_list[i]); + listPtr.elementAt(i).value = _list[i]; } return func(builder._cBuilder, _property._propertyId, listPtr, length); } finally { - listPtr.free(); + free(listPtr); } } // TODO replace duplication with implementation above, when fix is in int _opList64(QueryBuilder builder, obx_qb_cond_operator_in_dart_t func) { int length = _list.length; - final listPtr = Pointer.allocate(count: length); + final listPtr = allocate(count: length); try { for (int i = 0; i < length; i++) { - listPtr.elementAt(i).store(_list[i]); + listPtr.elementAt(i).value = _list[i]; } return func(builder._cBuilder, _property._propertyId, listPtr, length); } finally { - listPtr.free(); + free(listPtr); } } @@ -502,7 +503,7 @@ class ConditionGroup extends Condition { return _conditions[0].apply(builder, isRoot); } - final intArrayPtr = Pointer.allocate(count: size); + final intArrayPtr = allocate(count: size); try { for (int i = 0; i < size; ++i) { final cid = _conditions[i].apply(builder, false); @@ -511,7 +512,7 @@ class ConditionGroup extends Condition { throw Exception("Failed to create condition " + _conditions[i].toString()); } - intArrayPtr.elementAt(i).store(cid); + intArrayPtr.elementAt(i).value = cid; } // root All (AND) is implicit so no need to actually combine the conditions @@ -521,7 +522,7 @@ class ConditionGroup extends Condition { return _func(builder._cBuilder, intArrayPtr, size); } finally { - intArrayPtr.free(); + free(intArrayPtr); } } } @@ -545,12 +546,12 @@ class Query { } int count() { - final ptr = Pointer.allocate(count: 1); + final ptr = allocate(count: 1); try { checkObx(bindings.obx_query_count(_cQuery, ptr)); - return ptr.load(); + return ptr.value; } finally { - ptr.free(); + free(ptr); } } @@ -567,7 +568,7 @@ class Query { List findIds({int offset = 0, int limit = 0}) { final idArrayPtr = checkObxPtr(bindings.obx_query_find_ids(_cQuery, offset, limit), "find ids"); try { - OBX_id_array idArray = idArrayPtr.load(); + OBX_id_array idArray = idArrayPtr.ref; return idArray.length == 0 ? List() : idArray.items(); } finally { bindings.obx_id_array_free(idArrayPtr); diff --git a/lib/src/store.dart b/lib/src/store.dart index 46ba3d5da..9480c82a4 100644 --- a/lib/src/store.dart +++ b/lib/src/store.dart @@ -13,6 +13,8 @@ enum TxMode { Write, } +/// Represents an ObjectBox database and works together with [Box] to allow getting and putting Objects of +/// specific type. class Store { Pointer _cStore; Map _entityDefinitions = {}; @@ -27,11 +29,11 @@ class Store { try { checkObx(bindings.obx_opt_model(opt, model.ptr)); if (directory != null && directory.isNotEmpty) { - var cStr = Utf8.toUtf8(directory).cast(); + var cStr = Utf8.toUtf8(directory); try { - checkObx(bindings.obx_opt_directory(opt, cStr)); + checkObx(bindings.obx_opt_directory(opt, cStr.cast())); } finally { - cStr.free(); + free(cStr); } } if (maxDBSizeInKB != null && maxDBSizeInKB > 0) bindings.obx_opt_max_db_size_in_kb(opt, maxDBSizeInKB); @@ -45,6 +47,10 @@ class Store { checkObxPtr(_cStore, "failed to create store"); } + /// Closes this store. + /// + /// This method is useful for unit tests; most real applications should open a Store once and keep it open until + /// the app dies. close() { checkObx(bindings.obx_store_close(_cStore)); } @@ -53,9 +59,9 @@ class Store { return _entityDefinitions[T]; } - /// Executes a given function inside a transaction + /// Executes a given function inside a transaction. /// - /// Returns type of [fn] if [return] is called in [fn] + /// Returns type of [fn] if [return] is called in [fn]. R runInTransaction(TxMode mode, R Function() fn) { bool write = mode == TxMode.Write; Pointer txn = write ? bindings.obx_txn_write(_cStore) : bindings.obx_txn_read(_cStore); @@ -75,5 +81,6 @@ class Store { } } + /// The low-level pointer to this store. get ptr => _cStore; } diff --git a/pubspec.yaml b/pubspec.yaml index 80a90924c..6768a64df 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,17 @@ name: objectbox -version: 0.4.0 +version: 0.5.0 repository: https://github.com/objectbox/objectbox-dart homepage: https://objectbox.io author: objectbox.io description: ObjectBox is a super-fast NoSQL ACID compliant object database. environment: - sdk: '>=2.5.0 <2.6.0' + sdk: ">=2.6.0 <3.0.0" dependencies: - flat_buffers: 1.11.0 # take care updating flatbuffers - keep aligned with other bindings - ffi: 0.1.2 - + # take care updating flatbuffers - keep aligned with other bindings + flat_buffers: 1.11.0 + ffi: ^0.1.3 dev_dependencies: build_runner: ^1.0.0 objectbox_generator: diff --git a/test/test_env.dart b/test/test_env.dart index 64b7b10e5..b9e7d8a98 100644 --- a/test/test_env.dart +++ b/test/test_env.dart @@ -1,3 +1,4 @@ +import "dart:io"; import "package:objectbox/objectbox.dart"; import "entity.dart";