Migrate moor_ffi to Dart 2.6

This commit is contained in:
Simon Binder 2019-10-17 19:00:51 +02:00
parent d00336a1d2
commit aabf565479
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
9 changed files with 55 additions and 59 deletions

View File

@ -1,3 +1,6 @@
## 0.1.0-dev.1
- Support Dart 2.6 (starting from `2.6.0-dev.8.0`)
## 0.0.1 ## 0.0.1
- Initial release. Contains standalone bindings and a moor implementation. - Initial release. Contains standalone bindings and a moor implementation.

View File

@ -15,7 +15,7 @@ import 'dart:ffi';
/// is its destructor. There are many other interfaces (such as /// is its destructor. There are many other interfaces (such as
/// [sqlite3_prepare_v2()], [sqlite3_create_function()], and /// [sqlite3_prepare_v2()], [sqlite3_create_function()], and
/// [sqlite3_busy_timeout()] to name but three) that are methods on an /// [sqlite3_busy_timeout()] to name but three) that are methods on an
class Database extends Struct<Database> {} class Database extends Struct {}
/// SQL Statement Object /// SQL Statement Object
/// ///
@ -38,7 +38,7 @@ class Database extends Struct<Database> {}
/// ///
/// Refer to documentation on individual methods above for additional /// Refer to documentation on individual methods above for additional
/// information. /// information.
class Statement extends Struct<Statement> {} class Statement extends Struct {}
/// Dynamically Typed Value Object /// Dynamically Typed Value Object
/// ///
@ -74,4 +74,4 @@ class Statement extends Struct<Statement> {}
/// [sqlite3_result_value()] and [sqlite3_bind_value()]. /// [sqlite3_result_value()] and [sqlite3_bind_value()].
/// The [sqlite3_value_blob | sqlite3_value_type()] family of /// The [sqlite3_value_blob | sqlite3_value_type()] family of
/// interfaces require protected sqlite3_value objects. /// interfaces require protected sqlite3_value objects.
class Value extends Struct<Value> {} class Value extends Struct {}

View File

@ -7,14 +7,11 @@ import 'package:moor_ffi/src/ffi/utils.dart';
import 'package:ffi/ffi.dart' as ffi; import 'package:ffi/ffi.dart' as ffi;
/// Pointer to arbitrary blobs in C. /// Pointer to arbitrary blobs in C.
class CBlob extends Struct<CBlob> { class CBlob extends Struct {
@Uint8()
int data;
static Pointer<CBlob> allocate(Uint8List blob) { static Pointer<CBlob> allocate(Uint8List blob) {
final str = ffi.allocate<Uint8>(count: blob.length); final str = ffi.allocate<Uint8>(count: blob.length);
final asList = str.asExternalTypedData(count: blob.length) as Uint8List; final asList = str.asTypedList(blob.length);
asList.setAll(0, blob); asList.setAll(0, blob);
return str.cast(); return str.cast();
@ -28,37 +25,38 @@ class CBlob extends Struct<CBlob> {
..setAll(0, encoded); ..setAll(0, encoded);
return CBlob.allocate(data); return CBlob.allocate(data);
} }
}
/// Reads [bytesToRead] bytes from the current position. extension CBlobPointer on Pointer<CBlob> {
Uint8List read(int bytesToRead) {
assert(bytesToRead >= 0);
final str = addressOf.cast<Uint8>();
if (isNullPointer(str)) return null;
final data = str.asExternalTypedData(count: bytesToRead) as Uint8List;
return Uint8List.fromList(data);
}
/// More efficient version of [readString] that doesn't have to find a nil-
/// terminator. [length] is the amount of bytes to read. The string will be
/// decoded via [utf8].
String readAsStringWithLength(int length) {
return utf8.decode(read(length));
}
/// Reads a 0-terminated string, decoded with utf8. /// Reads a 0-terminated string, decoded with utf8.
/// ///
/// Warning: This method is very, very slow. If there is any way to know the /// Warning: This method is very, very slow. If there is any way to know the
/// length of the string to read, [readAsStringWithLength] will be orders of /// length of the string to read, [readAsStringWithLength] will be orders of
/// magnitude faster. /// magnitude faster.
String readString() { String readString() {
final str = addressOf; if (isNullPointer) return null;
if (isNullPointer(str)) return null;
var len = 0; var len = 0;
while (str.elementAt(++len).load<CBlob>().data != 0) {} final asUintPointer = cast<Uint8>();
while (asUintPointer[++len] != 0) {}
final units = read(len); final units = readBytes(len);
return utf8.decode(units); return utf8.decode(units);
} }
/// More efficient version of [readString] that doesn't have to find a nil-
/// terminator. [length] is the amount of bytes to read. The string will be
/// decoded via [utf8].
String readAsStringWithLength(int length) {
return utf8.decode(readBytes(length));
}
/// Reads [length] bytes from this address.
Uint8List readBytes(int length) {
assert(length >= 0);
if (isNullPointer) return null;
final data = cast<Uint8>().asTypedList(length);
return Uint8List.fromList(data);
}
} }

View File

@ -2,11 +2,10 @@ import 'dart:ffi';
import 'package:ffi/ffi.dart' as ffi; import 'package:ffi/ffi.dart' as ffi;
bool isNullPointer<T extends NativeType>(Pointer<T> ptr) => ptr == nullptr; extension FreePointerExtension on Pointer {
bool get isNullPointer => this == nullptr;
extension FreePointerExtension<T extends NativeType> on Pointer<T> { void free() {
// todo rename to "free" after https://github.com/dart-lang/sdk/issues/38860
void $free() {
ffi.free(this); ffi.free(this);
} }
} }

View File

@ -37,10 +37,10 @@ class Database implements BaseDatabase {
final resultCode = final resultCode =
bindings.sqlite3_open_v2(pathC, dbOut, _openingFlags, nullptr.cast()); bindings.sqlite3_open_v2(pathC, dbOut, _openingFlags, nullptr.cast());
final dbPointer = dbOut.load<Pointer<types.Database>>(); final dbPointer = dbOut.value;
dbOut.$free(); dbOut.free();
pathC.$free(); pathC.free();
if (resultCode == Errors.SQLITE_OK) { if (resultCode == Errors.SQLITE_OK) {
return Database._(dbPointer); return Database._(dbPointer);
@ -94,14 +94,14 @@ class Database implements BaseDatabase {
final result = final result =
bindings.sqlite3_exec(_db, sqlPtr, nullptr, nullptr, errorOut); bindings.sqlite3_exec(_db, sqlPtr, nullptr, nullptr, errorOut);
sqlPtr.$free(); sqlPtr.free();
final errorPtr = errorOut.load<Pointer<CBlob>>(); final errorPtr = errorOut.value;
errorOut.$free(); errorOut.free();
String errorMsg; String errorMsg;
if (!isNullPointer(errorPtr)) { if (!errorPtr.isNullPointer) {
errorMsg = errorPtr.load<CBlob>().readString(); errorMsg = errorPtr.readString();
// the message was allocated from sqlite, we need to free it // the message was allocated from sqlite, we need to free it
bindings.sqlite3_free(errorPtr.cast()); bindings.sqlite3_free(errorPtr.cast());
} }
@ -120,10 +120,10 @@ class Database implements BaseDatabase {
final resultCode = final resultCode =
bindings.sqlite3_prepare_v2(_db, sqlPtr, -1, stmtOut, nullptr.cast()); bindings.sqlite3_prepare_v2(_db, sqlPtr, -1, stmtOut, nullptr.cast());
sqlPtr.$free(); sqlPtr.free();
final stmt = stmtOut.load<Pointer<types.Statement>>(); final stmt = stmtOut.value;
stmtOut.$free(); stmtOut.free();
if (resultCode != Errors.SQLITE_OK) { if (resultCode != Errors.SQLITE_OK) {
// we don't need to worry about freeing the statement. If preparing the // we don't need to worry about freeing the statement. If preparing the

View File

@ -12,11 +12,11 @@ class SqliteException implements Exception {
// hold the error message string is managed internally. The application does // hold the error message string is managed internally. The application does
// not need to worry about freeing the result." // not need to worry about freeing the result."
// https://www.sqlite.org/c3ref/errcode.html // https://www.sqlite.org/c3ref/errcode.html
final dbMessage = bindings.sqlite3_errmsg(db).load<CBlob>().readString(); final dbMessage = bindings.sqlite3_errmsg(db).readString();
String explanation; String explanation;
if (code != null) { if (code != null) {
explanation = bindings.sqlite3_errstr(code).load<CBlob>().readString(); explanation = bindings.sqlite3_errstr(code).readString();
} }
return SqliteException(dbMessage, explanation); return SqliteException(dbMessage, explanation);

View File

@ -40,8 +40,7 @@ class PreparedStatement implements BasePreparedStatement {
for (var i = 0; i < columnCount; i++) { for (var i = 0; i < columnCount; i++) {
// name pointer doesn't need to be disposed, that happens when we finalize // name pointer doesn't need to be disposed, that happens when we finalize
names[i] = names[i] = bindings.sqlite3_column_name(_stmt, i).readString();
bindings.sqlite3_column_name(_stmt, i).load<CBlob>().readString();
} }
while (_step() == Errors.SQLITE_ROW) { while (_step() == Errors.SQLITE_ROW) {
@ -62,14 +61,10 @@ class PreparedStatement implements BasePreparedStatement {
final length = bindings.sqlite3_column_bytes(_stmt, index); final length = bindings.sqlite3_column_bytes(_stmt, index);
return bindings return bindings
.sqlite3_column_text(_stmt, index) .sqlite3_column_text(_stmt, index)
.load<CBlob>()
.readAsStringWithLength(length); .readAsStringWithLength(length);
case Types.SQLITE_BLOB: case Types.SQLITE_BLOB:
final length = bindings.sqlite3_column_bytes(_stmt, index); final length = bindings.sqlite3_column_bytes(_stmt, index);
return bindings return bindings.sqlite3_column_blob(_stmt, index).readBytes(length);
.sqlite3_column_blob(_stmt, index)
.load<CBlob>()
.read(length);
case Types.SQLITE_NULL: case Types.SQLITE_NULL:
default: default:
return null; return null;
@ -95,7 +90,7 @@ class PreparedStatement implements BasePreparedStatement {
_bound = false; _bound = false;
} }
for (var pointer in _allocatedWhileBinding) { for (var pointer in _allocatedWhileBinding) {
pointer.$free(); pointer.free();
} }
_allocatedWhileBinding.clear(); _allocatedWhileBinding.clear();
} }

View File

@ -1,16 +1,16 @@
name: moor_ffi name: moor_ffi
description: "Provides sqlite bindings using dart:ffi, including a moor executor" description: "Provides sqlite bindings using dart:ffi, including a moor executor"
version: 0.1.0-dev version: 0.1.0-dev.1
author: Simon Binder <oss@simonbinder.eu> author: Simon Binder <oss@simonbinder.eu>
homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
issue_tracker: https://github.com/simolus3/moor/issues issue_tracker: https://github.com/simolus3/moor/issues
environment: environment:
sdk: ">=2.6.0-dev <3.0.0" sdk: ">=2.6.0-dev.8.0 <3.0.0"
dependencies: dependencies:
moor: ">=1.7.0 <3.0.0" moor: ">=1.7.0 <3.0.0"
ffi: 0.1.3-dev.1 # todo replace with carret after the next dev release, then non-dev version once Dart 2.6 is out ffi: ^0.1.3-dev.3 # todo non-dev version once Dart 2.6 is out
collection: ^1.0.0 collection: ^1.0.0
dev_dependencies: dev_dependencies:

View File

@ -1,5 +1,6 @@
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'package:moor_ffi/src/ffi/blob.dart'; import 'package:moor_ffi/src/ffi/blob.dart';
import 'package:moor_ffi/src/ffi/utils.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
@ -7,7 +8,7 @@ void main() {
final content = 'Hasta Mañana'; final content = 'Hasta Mañana';
final blob = CBlob.allocateString(content); final blob = CBlob.allocateString(content);
expect(blob.load<CBlob>().readString(), content); expect(blob.readString(), content);
blob.free(); blob.free();
}); });
@ -15,7 +16,7 @@ void main() {
final data = List.generate(256, (x) => x); final data = List.generate(256, (x) => x);
final blob = CBlob.allocate(Uint8List.fromList(data)); final blob = CBlob.allocate(Uint8List.fromList(data));
expect(blob.load<CBlob>().read(256), data); expect(blob.readBytes(256), data);
blob.free(); blob.free();
}); });
} }