mirror of https://github.com/AMT-Cheif/drift.git
Migrate moor_ffi to Dart 2.6
This commit is contained in:
parent
d00336a1d2
commit
aabf565479
|
@ -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.
|
|
@ -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 {}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue