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
- 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
/// [sqlite3_prepare_v2()], [sqlite3_create_function()], and
/// [sqlite3_busy_timeout()] to name but three) that are methods on an
class Database extends Struct<Database> {}
class Database extends Struct {}
/// SQL Statement Object
///
@ -38,7 +38,7 @@ class Database extends Struct<Database> {}
///
/// Refer to documentation on individual methods above for additional
/// information.
class Statement extends Struct<Statement> {}
class Statement extends Struct {}
/// Dynamically Typed Value Object
///
@ -74,4 +74,4 @@ class Statement extends Struct<Statement> {}
/// [sqlite3_result_value()] and [sqlite3_bind_value()].
/// The [sqlite3_value_blob | sqlite3_value_type()] family of
/// 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;
/// Pointer to arbitrary blobs in C.
class CBlob extends Struct<CBlob> {
@Uint8()
int data;
class CBlob extends Struct {
static Pointer<CBlob> allocate(Uint8List blob) {
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);
return str.cast();
@ -28,37 +25,38 @@ class CBlob extends Struct<CBlob> {
..setAll(0, encoded);
return CBlob.allocate(data);
}
}
/// Reads [bytesToRead] bytes from the current position.
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));
}
extension CBlobPointer on Pointer<CBlob> {
/// Reads a 0-terminated string, decoded with utf8.
///
/// 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
/// magnitude faster.
String readString() {
final str = addressOf;
if (isNullPointer(str)) return null;
if (isNullPointer) return null;
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);
}
/// 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;
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> {
// todo rename to "free" after https://github.com/dart-lang/sdk/issues/38860
void $free() {
void free() {
ffi.free(this);
}
}

View File

@ -37,10 +37,10 @@ class Database implements BaseDatabase {
final resultCode =
bindings.sqlite3_open_v2(pathC, dbOut, _openingFlags, nullptr.cast());
final dbPointer = dbOut.load<Pointer<types.Database>>();
final dbPointer = dbOut.value;
dbOut.$free();
pathC.$free();
dbOut.free();
pathC.free();
if (resultCode == Errors.SQLITE_OK) {
return Database._(dbPointer);
@ -94,14 +94,14 @@ class Database implements BaseDatabase {
final result =
bindings.sqlite3_exec(_db, sqlPtr, nullptr, nullptr, errorOut);
sqlPtr.$free();
sqlPtr.free();
final errorPtr = errorOut.load<Pointer<CBlob>>();
errorOut.$free();
final errorPtr = errorOut.value;
errorOut.free();
String errorMsg;
if (!isNullPointer(errorPtr)) {
errorMsg = errorPtr.load<CBlob>().readString();
if (!errorPtr.isNullPointer) {
errorMsg = errorPtr.readString();
// the message was allocated from sqlite, we need to free it
bindings.sqlite3_free(errorPtr.cast());
}
@ -120,10 +120,10 @@ class Database implements BaseDatabase {
final resultCode =
bindings.sqlite3_prepare_v2(_db, sqlPtr, -1, stmtOut, nullptr.cast());
sqlPtr.$free();
sqlPtr.free();
final stmt = stmtOut.load<Pointer<types.Statement>>();
stmtOut.$free();
final stmt = stmtOut.value;
stmtOut.free();
if (resultCode != Errors.SQLITE_OK) {
// 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
// not need to worry about freeing the result."
// 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;
if (code != null) {
explanation = bindings.sqlite3_errstr(code).load<CBlob>().readString();
explanation = bindings.sqlite3_errstr(code).readString();
}
return SqliteException(dbMessage, explanation);

View File

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

View File

@ -1,16 +1,16 @@
name: moor_ffi
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>
homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
issue_tracker: https://github.com/simolus3/moor/issues
environment:
sdk: ">=2.6.0-dev <3.0.0"
sdk: ">=2.6.0-dev.8.0 <3.0.0"
dependencies:
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
dev_dependencies:

View File

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