mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 16:00:45 +00:00
[vm/ffi] Migrate samples(_2)/ffi to CallocAllocator
This CL does not yet roll `package:ffi` to use `Allocator`, because that breaks the checked in Dart in Flutter in g3. Instead, this copies `_CallocAllocator` from `package:ffi` into the samples. New API landed in: https://dart-review.googlesource.com/c/sdk/+/177705 Issue: https://github.com/dart-lang/sdk/issues/44621 Issue: https://github.com/dart-lang/sdk/issues/38721 Change-Id: I83da349c2e52d7f079aa1569b4726318fee24c9d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177706 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Aske Simon Christensen <askesc@google.com>
This commit is contained in:
parent
978b838461
commit
d37e833e23
109
samples/ffi/calloc.dart
Normal file
109
samples/ffi/calloc.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be
|
||||
// rolled. We need to wait until the `Allocator` interface has rolled into
|
||||
// Flutter.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary stdlib = Platform.isWindows
|
||||
? DynamicLibrary.open('kernel32.dll')
|
||||
: DynamicLibrary.process();
|
||||
|
||||
typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size);
|
||||
typedef PosixCalloc = Pointer Function(int num, int size);
|
||||
final PosixCalloc posixCalloc =
|
||||
stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
|
||||
|
||||
typedef WinHeapFreeNative = Int32 Function(
|
||||
Pointer heap, Uint32 flags, Pointer memory);
|
||||
typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
|
||||
final WinHeapFree winHeapFree =
|
||||
stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
|
||||
|
||||
const int HEAP_ZERO_MEMORY = 8;
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
class _CallocAllocator implements Allocator {
|
||||
const _CallocAllocator();
|
||||
|
||||
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
|
||||
/// heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc`. On Windows, it uses
|
||||
/// `HeapAlloc` against the default public heap.
|
||||
///
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> result;
|
||||
if (Platform.isWindows) {
|
||||
result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
|
||||
.cast();
|
||||
} else {
|
||||
result = posixCalloc(byteCount, 1).cast();
|
||||
}
|
||||
if (result.address == 0) {
|
||||
throw ArgumentError('Could not allocate $byteCount bytes.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Releases memory allocated on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
|
||||
/// freed.
|
||||
///
|
||||
// TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead
|
||||
// of testing the return integer to be non-zero.
|
||||
@override
|
||||
void free(Pointer pointer) {
|
||||
if (Platform.isWindows) {
|
||||
if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
|
||||
throw ArgumentError('Could not free $pointer.');
|
||||
}
|
||||
} else {
|
||||
posixFree(pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero. Use [malloc] for unintialized
|
||||
/// memory allocation.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
const Allocator calloc = _CallocAllocator();
|
|
@ -16,8 +16,9 @@ class Coordinate extends Struct {
|
|||
|
||||
external Pointer<Coordinate> next;
|
||||
|
||||
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
|
||||
return allocate<Coordinate>().ref
|
||||
factory Coordinate.allocate(
|
||||
Allocator allocator, double x, double y, Pointer<Coordinate> next) {
|
||||
return allocator<Coordinate>().ref
|
||||
..x = x
|
||||
..y = y
|
||||
..next = next;
|
||||
|
|
|
@ -5,48 +5,36 @@
|
|||
// Explicit pool used for managing resources.
|
||||
|
||||
import "dart:async";
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart' as packageFfi;
|
||||
import 'package:ffi/ffi.dart' show Utf8;
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
/// Manages native resources.
|
||||
import '../calloc.dart';
|
||||
|
||||
/// Keeps track of all allocated memory and frees all allocated memory on
|
||||
/// [releaseAll].
|
||||
///
|
||||
/// Primary implementations are [Pool] and [Unmanaged].
|
||||
abstract class ResourceManager {
|
||||
/// Allocates memory on the native heap.
|
||||
///
|
||||
/// The native memory is under management by this [ResourceManager].
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count: 1});
|
||||
}
|
||||
/// Wraps an [Allocator] to do the actual allocation and freeing.
|
||||
class Pool implements Allocator {
|
||||
/// The [Allocator] used for allocation and freeing.
|
||||
final Allocator _wrappedAllocator;
|
||||
|
||||
Pool(this._wrappedAllocator);
|
||||
|
||||
/// Manages native resources.
|
||||
class Pool implements ResourceManager {
|
||||
/// Native memory under management by this [Pool].
|
||||
final List<Pointer<NativeType>> _managedMemoryPointers = [];
|
||||
|
||||
/// Callbacks for releasing native resources under management by this [Pool].
|
||||
final List<Function()> _managedResourceReleaseCallbacks = [];
|
||||
|
||||
/// Allocates memory on the native heap.
|
||||
/// Allocates memory on the native heap by using the allocator supplied to
|
||||
/// the constructor.
|
||||
///
|
||||
/// The native memory is under management by this [Pool].
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count: 1}) {
|
||||
final p = Unmanaged().allocate<T>(count: count);
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int numBytes, {int? alignment}) {
|
||||
final p = _wrappedAllocator.allocate<T>(numBytes, alignment: alignment);
|
||||
_managedMemoryPointers.add(p);
|
||||
return p;
|
||||
}
|
||||
|
@ -71,17 +59,21 @@ class Pool implements ResourceManager {
|
|||
}
|
||||
_managedResourceReleaseCallbacks.clear();
|
||||
for (final p in _managedMemoryPointers) {
|
||||
Unmanaged().free(p);
|
||||
_wrappedAllocator.free(p);
|
||||
}
|
||||
_managedMemoryPointers.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void free(Pointer<NativeType> pointer) => throw UnsupportedError(
|
||||
"Individually freeing Pool allocated memory is not allowed");
|
||||
}
|
||||
|
||||
/// Creates a [Pool] to manage native resources.
|
||||
///
|
||||
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
|
||||
R using<R>(R Function(Pool) f) {
|
||||
final p = Pool();
|
||||
R using<R>(R Function(Pool) f, [Allocator wrappedAllocator = calloc]) {
|
||||
final p = Pool(wrappedAllocator);
|
||||
try {
|
||||
return f(p);
|
||||
} finally {
|
||||
|
@ -96,8 +88,8 @@ R using<R>(R Function(Pool) f) {
|
|||
/// Please note that all throws are caught and packaged in [RethrownError].
|
||||
///
|
||||
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
|
||||
R usePool<R>(R Function() f) {
|
||||
final p = Pool();
|
||||
R usePool<R>(R Function() f, [Allocator wrappedAllocator = calloc]) {
|
||||
final p = Pool(wrappedAllocator);
|
||||
try {
|
||||
return runZoned(() => f(),
|
||||
zoneValues: {#_pool: p},
|
||||
|
@ -117,78 +109,3 @@ class RethrownError {
|
|||
toString() => """RethrownError(${original})
|
||||
${originalStackTrace}""";
|
||||
}
|
||||
|
||||
/// Does not manage it's resources.
|
||||
class Unmanaged implements ResourceManager {
|
||||
/// Allocates memory on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count = 1}) =>
|
||||
packageFfi.allocate(count: count);
|
||||
|
||||
/// Releases memory on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses free. On Windows, it uses HeapFree
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an ArgumentError on failure to free.
|
||||
///
|
||||
void free(Pointer pointer) => packageFfi.free(pointer);
|
||||
}
|
||||
|
||||
/// Does not manage it's resources.
|
||||
final Unmanaged unmanaged = Unmanaged();
|
||||
|
||||
extension Utf8InPool on String {
|
||||
/// Convert a [String] to a Utf8-encoded null-terminated C string.
|
||||
///
|
||||
/// If 'string' contains NULL bytes, the converted string will be truncated
|
||||
/// prematurely. Unpaired surrogate code points in [string] will be preserved
|
||||
/// in the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
|
||||
///
|
||||
/// Returns a malloc-allocated pointer to the result.
|
||||
///
|
||||
/// The memory is managed by the [Pool] passed in as [pool].
|
||||
Pointer<Utf8> toUtf8(ResourceManager pool) {
|
||||
final units = utf8.encode(this);
|
||||
final Pointer<Uint8> result = pool.allocate<Uint8>(count: units.length + 1);
|
||||
final Uint8List nativeString = result.asTypedList(units.length + 1);
|
||||
nativeString.setAll(0, units);
|
||||
nativeString[units.length] = 0;
|
||||
return result.cast();
|
||||
}
|
||||
}
|
||||
|
||||
extension Utf8Helpers on Pointer<Utf8> {
|
||||
/// Returns the length of a null-terminated string -- the number of (one-byte)
|
||||
/// characters before the first null byte.
|
||||
int strlen() {
|
||||
final Pointer<Uint8> array = this.cast<Uint8>();
|
||||
final Uint8List nativeString = array.asTypedList(_maxSize);
|
||||
return nativeString.indexWhere((char) => char == 0);
|
||||
}
|
||||
|
||||
/// Creates a [String] containing the characters UTF-8 encoded in [this].
|
||||
///
|
||||
/// [this] must be a zero-terminated byte sequence of valid UTF-8
|
||||
/// encodings of Unicode code points. It may also contain UTF-8 encodings of
|
||||
/// unpaired surrogate code points, which is not otherwise valid UTF-8, but
|
||||
/// which may be created when encoding a Dart string containing an unpaired
|
||||
/// surrogate. See [Utf8Decoder] for details on decoding.
|
||||
///
|
||||
/// Returns a Dart string containing the decoded code points.
|
||||
String contents() {
|
||||
final int length = strlen();
|
||||
return utf8.decode(Uint8List.view(
|
||||
this.cast<Uint8>().asTypedList(length).buffer, 0, length));
|
||||
}
|
||||
}
|
||||
|
||||
const int _kMaxSmi64 = (1 << 62) - 1;
|
||||
const int _kMaxSmi32 = (1 << 30) - 1;
|
||||
final int _maxSize = sizeOf<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'dart:ffi';
|
|||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
|
@ -21,7 +22,7 @@ main() {
|
|||
|
||||
// To ensure resources are freed, wrap them in a [using] call.
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
|
@ -31,14 +32,14 @@ main() {
|
|||
// Resources are freed also when abnormal control flow occurs.
|
||||
try {
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
p[0] = 25;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
|
||||
print(p[1]);
|
||||
Expect.equals(25, p[1]);
|
||||
throw Exception("Some random exception");
|
||||
});
|
||||
// `free(p)` has been called.
|
||||
// `calloc.free(p)` has been called.
|
||||
} on Exception catch (e) {
|
||||
print("Caught exception: $e");
|
||||
}
|
||||
|
@ -46,8 +47,8 @@ main() {
|
|||
// In a pool multiple resources can be allocated, which will all be freed
|
||||
// at the end of the scope.
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p2 = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
final p2 = pool<Int64>(2);
|
||||
p[0] = 1;
|
||||
p[1] = 2;
|
||||
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
|
||||
|
@ -58,7 +59,7 @@ main() {
|
|||
// If the resource allocation happens in a different scope, then one either
|
||||
// needs to pass the pool to that scope.
|
||||
f1(Pool pool) {
|
||||
return pool.allocate<Int64>(count: 2);
|
||||
return pool<Int64>(2);
|
||||
}
|
||||
|
||||
using((Pool pool) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'dart:ffi';
|
|||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
|
@ -21,7 +22,7 @@ main() {
|
|||
|
||||
// To ensure resources are freed, wrap them in a [using] call.
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
|
@ -31,7 +32,7 @@ main() {
|
|||
// Resources are freed also when abnormal control flow occurs.
|
||||
try {
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
p[0] = 25;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
|
||||
print(p[1]);
|
||||
|
@ -46,8 +47,8 @@ main() {
|
|||
// In a pool multiple resources can be allocated, which will all be freed
|
||||
// at the end of the scope.
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p2 = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
final p2 = currentPool<Int64>(2);
|
||||
p[0] = 1;
|
||||
p[1] = 2;
|
||||
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
|
||||
|
@ -58,7 +59,7 @@ main() {
|
|||
// If the resource allocation happens in a different scope, it is in the
|
||||
// same zone, so it's lifetime is automatically managed by the pool.
|
||||
f1() {
|
||||
return currentPool.allocate<Int64>(count: 2);
|
||||
return currentPool<Int64>(2);
|
||||
}
|
||||
|
||||
usePool(() {
|
||||
|
|
|
@ -7,30 +7,32 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../calloc.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
final ffiTestDynamicLibrary =
|
||||
dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||
|
||||
final MemMove = ffiTestDynamicLibrary.lookupFunction<
|
||||
final memMove = ffiTestDynamicLibrary.lookupFunction<
|
||||
Void Function(Pointer<Void>, Pointer<Void>, IntPtr),
|
||||
void Function(Pointer<Void>, Pointer<Void>, int)>("MemMove");
|
||||
|
||||
// To ensure resources are freed, call free manually.
|
||||
//
|
||||
// For automatic management use a Pool.
|
||||
final p = unmanaged.allocate<Int64>(count: 2);
|
||||
final p = calloc<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
memMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
Expect.equals(24, p[1]);
|
||||
unmanaged.free(p);
|
||||
calloc.free(p);
|
||||
|
||||
// Using Strings.
|
||||
final p2 = "Hello world!".toUtf8(unmanaged);
|
||||
final p2 = "Hello world!".toUtf8(calloc);
|
||||
print(p2.contents());
|
||||
unmanaged.free(p2);
|
||||
calloc.free(p2);
|
||||
}
|
||||
|
|
53
samples/ffi/resource_management/utf8_helpers.dart
Normal file
53
samples/ffi/resource_management/utf8_helpers.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
extension Utf8InPool on String {
|
||||
/// Convert a [String] to a Utf8-encoded null-terminated C string.
|
||||
///
|
||||
/// If 'string' contains NULL bytes, the converted string will be truncated
|
||||
/// prematurely. See [Utf8Encoder] for details on encoding.
|
||||
///
|
||||
/// Returns a [allocator]-allocated pointer to the result.
|
||||
Pointer<Utf8> toUtf8(Allocator allocator) {
|
||||
final units = utf8.encode(this);
|
||||
final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
|
||||
final Uint8List nativeString = result.asTypedList(units.length + 1);
|
||||
nativeString.setAll(0, units);
|
||||
nativeString[units.length] = 0;
|
||||
return result.cast();
|
||||
}
|
||||
}
|
||||
|
||||
extension Utf8Helpers on Pointer<Utf8> {
|
||||
/// The length of a null-terminated string — the number of (one-byte)
|
||||
/// characters before the first null byte.
|
||||
int get strlen {
|
||||
final Pointer<Uint8> array = this.cast<Uint8>();
|
||||
final Uint8List nativeString = array.asTypedList(_maxSize);
|
||||
return nativeString.indexWhere((char) => char == 0);
|
||||
}
|
||||
|
||||
/// Creates a [String] containing the characters UTF-8 encoded in [this].
|
||||
///
|
||||
/// [this] must be a zero-terminated byte sequence of valid UTF-8
|
||||
/// encodings of Unicode code points. See [Utf8Decoder] for details on
|
||||
/// decoding.
|
||||
///
|
||||
/// Returns a Dart string containing the decoded code points.
|
||||
String contents() {
|
||||
final int length = strlen;
|
||||
return utf8.decode(Uint8List.view(
|
||||
this.cast<Uint8>().asTypedList(length).buffer, 0, length));
|
||||
}
|
||||
}
|
||||
|
||||
const int _kMaxSmi64 = (1 << 62) - 1;
|
||||
const int _kMaxSmi32 = (1 << 30) - 1;
|
||||
final int _maxSize = sizeOf<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
|
|
@ -7,6 +7,8 @@ import 'dart:ffi';
|
|||
import 'package:ffi/ffi.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
/// typedef struct {
|
||||
/// unsigned int bold : 1;
|
||||
/// unsigned int underline : 2;
|
||||
|
@ -93,7 +95,7 @@ extension IntBitField on int {
|
|||
}
|
||||
|
||||
main() {
|
||||
final p = allocate<ScreenCellAttrs>(count: 3);
|
||||
final p = calloc<ScreenCellAttrs>(3);
|
||||
|
||||
// Zeroes out all fields.
|
||||
p.ref.bits = 0;
|
||||
|
@ -119,5 +121,5 @@ main() {
|
|||
// A check for automated testing.
|
||||
Expect.equals(1933, p.ref.bits);
|
||||
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
|
|
@ -5,29 +5,31 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
main() {
|
||||
print('start main');
|
||||
|
||||
{
|
||||
// Basic operation: allocate, get, set, and free.
|
||||
Pointer<Int64> p = allocate();
|
||||
Pointer<Int64> p = calloc();
|
||||
p.value = 42;
|
||||
int pValue = p.value;
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Undefined behavior before set.
|
||||
Pointer<Int64> p = allocate();
|
||||
Pointer<Int64> p = calloc();
|
||||
int pValue = p.value;
|
||||
print('If not set, returns garbage: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Pointers can be created from an address.
|
||||
Pointer<Int64> pHelper = allocate();
|
||||
Pointer<Int64> pHelper = calloc();
|
||||
pHelper.value = 1337;
|
||||
|
||||
int address = pHelper.address;
|
||||
|
@ -36,13 +38,13 @@ main() {
|
|||
Pointer<Int64> p = Pointer.fromAddress(address);
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
|
||||
free(pHelper);
|
||||
calloc.free(pHelper);
|
||||
}
|
||||
|
||||
{
|
||||
// Address is zeroed out after free.
|
||||
Pointer<Int64> p = allocate();
|
||||
free(p);
|
||||
Pointer<Int64> p = calloc();
|
||||
calloc.free(p);
|
||||
print('After free, address is zero: ${p.address}');
|
||||
}
|
||||
|
||||
|
@ -50,13 +52,13 @@ main() {
|
|||
// Allocating too much throws an exception.
|
||||
try {
|
||||
int maxMint = 9223372036854775807; // 2^63 - 1
|
||||
allocate<Int64>(count: maxMint);
|
||||
calloc<Int64>(maxMint);
|
||||
} on Error {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
try {
|
||||
int maxInt1_8 = 1152921504606846975; // 2^60 -1
|
||||
allocate<Int64>(count: maxInt1_8);
|
||||
calloc<Int64>(maxInt1_8);
|
||||
} on Error {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ main() {
|
|||
{
|
||||
// Pointers can be cast into another type,
|
||||
// resulting in the corresponding bits read.
|
||||
Pointer<Int64> p1 = allocate();
|
||||
Pointer<Int64> p1 = calloc();
|
||||
p1.value = 9223372036854775807; // 2^63 - 1
|
||||
|
||||
Pointer<Int32> p2 = p1.cast();
|
||||
|
@ -74,61 +76,61 @@ main() {
|
|||
Pointer<Int32> p3 = p2.elementAt(1);
|
||||
print('${p3.runtimeType} value: ${p3.value}'); // 2^31 - 1
|
||||
|
||||
free(p1);
|
||||
calloc.free(p1);
|
||||
}
|
||||
|
||||
{
|
||||
// Data can be tightly packed in memory.
|
||||
Pointer<Int8> p = allocate(count: 8);
|
||||
Pointer<Int8> p = calloc(8);
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
p.elementAt(i).value = i * 3;
|
||||
}
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
print('p.elementAt($i) value: ${p.elementAt(i).value}');
|
||||
}
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Values that don't fit are truncated.
|
||||
Pointer<Int32> p11 = allocate();
|
||||
Pointer<Int32> p11 = calloc();
|
||||
|
||||
p11.value = 9223372036854775807;
|
||||
|
||||
print(p11);
|
||||
|
||||
free(p11);
|
||||
calloc.free(p11);
|
||||
}
|
||||
|
||||
{
|
||||
// Doubles.
|
||||
Pointer<Double> p = allocate();
|
||||
Pointer<Double> p = calloc();
|
||||
p.value = 3.14159265359;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
p.value = 3.14;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Floats.
|
||||
Pointer<Float> p = allocate();
|
||||
Pointer<Float> p = calloc();
|
||||
p.value = 3.14159265359;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
p.value = 3.14;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// IntPtr varies in size based on whether the platform is 32 or 64 bit.
|
||||
// Addresses of pointers fit in this size.
|
||||
Pointer<IntPtr> p = allocate();
|
||||
Pointer<IntPtr> p = calloc();
|
||||
int p14addr = p.address;
|
||||
p.value = p14addr;
|
||||
int pValue = p.value;
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -136,19 +138,19 @@ main() {
|
|||
// The size of the element it is pointing to is undefined,
|
||||
// they cannot be allocated, read, or written.
|
||||
|
||||
Pointer<IntPtr> p1 = allocate();
|
||||
Pointer<IntPtr> p1 = calloc();
|
||||
Pointer<Void> p2 = p1.cast();
|
||||
print('${p2.runtimeType} address: ${p2.address}');
|
||||
|
||||
free(p1);
|
||||
calloc.free(p1);
|
||||
}
|
||||
|
||||
{
|
||||
// Pointer to a pointer to something.
|
||||
Pointer<Int16> pHelper = allocate();
|
||||
Pointer<Int16> pHelper = calloc();
|
||||
pHelper.value = 17;
|
||||
|
||||
Pointer<Pointer<Int16>> p = allocate();
|
||||
Pointer<Pointer<Int16>> p = calloc();
|
||||
|
||||
// Storing into a pointer pointer automatically unboxes.
|
||||
p.value = pHelper;
|
||||
|
@ -160,31 +162,31 @@ main() {
|
|||
int pValue = p.value.value;
|
||||
print('${p.runtimeType} value\'s value: ${pValue}');
|
||||
|
||||
free(p);
|
||||
free(pHelper);
|
||||
calloc.free(p);
|
||||
calloc.free(pHelper);
|
||||
}
|
||||
|
||||
{
|
||||
// The pointer to pointer types must match up.
|
||||
Pointer<Int8> pHelper = allocate();
|
||||
Pointer<Int8> pHelper = calloc();
|
||||
pHelper.value = 123;
|
||||
|
||||
Pointer<Pointer<Int16>> p = allocate();
|
||||
Pointer<Pointer<Int16>> p = calloc();
|
||||
|
||||
// Trying to store `pHelper` into `p.val` would result in a type mismatch.
|
||||
|
||||
free(pHelper);
|
||||
free(p);
|
||||
calloc.free(pHelper);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// `nullptr` points to address 0 in c++.
|
||||
Pointer<Pointer<Int8>> pointerToPointer = allocate();
|
||||
Pointer<Pointer<Int8>> pointerToPointer = calloc();
|
||||
Pointer<Int8> value = nullptr;
|
||||
pointerToPointer.value = value;
|
||||
value = pointerToPointer.value;
|
||||
print("Loading a pointer to the 0 address is null: ${value}");
|
||||
free(pointerToPointer);
|
||||
calloc.free(pointerToPointer);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -205,7 +207,7 @@ main() {
|
|||
head.value = value;
|
||||
return;
|
||||
}
|
||||
Pointer<IntPtr> next = allocate<IntPtr>();
|
||||
Pointer<IntPtr> next = calloc<IntPtr>();
|
||||
head.value = next.address;
|
||||
createChain(next, length - 1, value);
|
||||
}
|
||||
|
@ -220,7 +222,7 @@ main() {
|
|||
|
||||
void freeChain(Pointer<IntPtr> head, int length) {
|
||||
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
|
||||
free(head);
|
||||
calloc.free(head);
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -228,7 +230,7 @@ main() {
|
|||
}
|
||||
|
||||
int length = 10;
|
||||
Pointer<IntPtr> head = allocate();
|
||||
Pointer<IntPtr> head = calloc();
|
||||
createChain(head, length, 512);
|
||||
int tailValue = getChainValue(head, length);
|
||||
print('tailValue: ${tailValue}');
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:ffi';
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
typedef NativeUnaryOp = Int32 Function(Int32);
|
||||
|
@ -184,7 +185,7 @@ main() {
|
|||
// pass an array / pointer as argument
|
||||
Int64PointerUnOp assign1337Index1 = ffiTestFunctions
|
||||
.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
|
||||
Pointer<Int64> p2 = allocate(count: 2);
|
||||
Pointer<Int64> p2 = calloc(2);
|
||||
p2.value = 42;
|
||||
p2[1] = 1000;
|
||||
print(p2.elementAt(1).address.toRadixString(16));
|
||||
|
@ -209,11 +210,11 @@ main() {
|
|||
print(result);
|
||||
print(result.runtimeType);
|
||||
|
||||
Pointer<Int64> p2 = allocate(count: 2);
|
||||
Pointer<Int64> p2 = calloc(2);
|
||||
result = nullableInt64ElemAt1(p2);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
free(p2);
|
||||
calloc.free(p2);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
|
@ -43,7 +46,7 @@ main() {
|
|||
Pointer<NativeFunction<CoordinateTrice>> p2 =
|
||||
ffiTestFunctions.lookup("CoordinateUnOpTrice");
|
||||
CoordinateTrice coordinateUnOpTrice = p2.asFunction();
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 20.0, nullptr);
|
||||
c1.next = c1.addressOf;
|
||||
Coordinate result =
|
||||
coordinateUnOpTrice(transposeCoordinatePointer, c1.addressOf).ref;
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
|
||||
|
||||
main() {
|
||||
|
@ -23,8 +24,8 @@ main() {
|
|||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(42.0, 84.0, c1.addressOf);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 20.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(calloc, 42.0, 84.0, c1.addressOf);
|
||||
c1.next = c2.addressOf;
|
||||
|
||||
Coordinate result = f1(c1.addressOf).ref;
|
||||
|
@ -44,7 +45,7 @@ main() {
|
|||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
|
||||
Pointer<Coordinate> c1 = calloc<Coordinate>(3);
|
||||
Pointer<Coordinate> c2 = c1.elementAt(1);
|
||||
Pointer<Coordinate> c3 = c1.elementAt(2);
|
||||
c1.ref.x = 10.0;
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:ffi';
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
|
||||
main() {
|
||||
|
@ -13,9 +14,9 @@ main() {
|
|||
|
||||
{
|
||||
// Allocates each coordinate separately in c memory.
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 10.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(20.0, 20.0, c1.addressOf);
|
||||
Coordinate c3 = Coordinate.allocate(30.0, 30.0, c2.addressOf);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 10.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(calloc, 20.0, 20.0, c1.addressOf);
|
||||
Coordinate c3 = Coordinate.allocate(calloc, 30.0, 30.0, c2.addressOf);
|
||||
c1.next = c3.addressOf;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
|
@ -24,14 +25,14 @@ main() {
|
|||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
free(c1.addressOf);
|
||||
free(c2.addressOf);
|
||||
free(c3.addressOf);
|
||||
calloc.free(c1.addressOf);
|
||||
calloc.free(c2.addressOf);
|
||||
calloc.free(c3.addressOf);
|
||||
}
|
||||
|
||||
{
|
||||
// Allocates coordinates consecutively in c memory.
|
||||
Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
|
||||
Pointer<Coordinate> c1 = calloc<Coordinate>(3);
|
||||
Pointer<Coordinate> c2 = c1.elementAt(1);
|
||||
Pointer<Coordinate> c3 = c1.elementAt(2);
|
||||
c1.ref.x = 10.0;
|
||||
|
@ -50,15 +51,15 @@ main() {
|
|||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
free(c1);
|
||||
calloc.free(c1);
|
||||
}
|
||||
|
||||
{
|
||||
Coordinate c = Coordinate.allocate(10, 10, nullptr);
|
||||
Coordinate c = Coordinate.allocate(calloc, 10, 10, nullptr);
|
||||
print(c is Coordinate);
|
||||
print(c is Pointer<Void>);
|
||||
print(c is Pointer);
|
||||
free(c.addressOf);
|
||||
calloc.free(c.addressOf);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
library sqlite;
|
||||
|
||||
export "src/database.dart";
|
||||
|
||||
export "src/ffi/calloc.dart" show calloc;
|
||||
|
|
|
@ -15,6 +15,8 @@ import "bindings/types.dart" hide Database;
|
|||
import "bindings/constants.dart";
|
||||
import "collections/closable_iterator.dart";
|
||||
|
||||
import 'ffi/calloc.dart';
|
||||
|
||||
/// [Database] represents an open connection to a SQLite database.
|
||||
///
|
||||
/// All functions against a database may throw [SQLiteError].
|
||||
|
@ -27,13 +29,13 @@ class Database {
|
|||
/// Open a database located at the file [path].
|
||||
Database(String path,
|
||||
[int flags = Flags.SQLITE_OPEN_READWRITE | Flags.SQLITE_OPEN_CREATE]) {
|
||||
Pointer<Pointer<types.Database>> dbOut = allocate();
|
||||
Pointer<Pointer<types.Database>> dbOut = calloc();
|
||||
final pathC = Utf8.toUtf8(path);
|
||||
final int resultCode =
|
||||
bindings.sqlite3_open_v2(pathC, dbOut, flags, nullptr);
|
||||
_database = dbOut.value;
|
||||
free(dbOut);
|
||||
free(pathC);
|
||||
calloc.free(dbOut);
|
||||
calloc.free(pathC);
|
||||
|
||||
if (resultCode == Errors.SQLITE_OK) {
|
||||
_open = true;
|
||||
|
@ -63,13 +65,13 @@ class Database {
|
|||
|
||||
/// Execute a query, discarding any returned rows.
|
||||
void execute(String query) {
|
||||
Pointer<Pointer<Statement>> statementOut = allocate();
|
||||
Pointer<Pointer<Statement>> statementOut = calloc();
|
||||
Pointer<Utf8> queryC = Utf8.toUtf8(query);
|
||||
int resultCode = bindings.sqlite3_prepare_v2(
|
||||
_database, queryC, -1, statementOut, nullptr);
|
||||
Pointer<Statement> statement = statementOut.value;
|
||||
free(statementOut);
|
||||
free(queryC);
|
||||
calloc.free(statementOut);
|
||||
calloc.free(queryC);
|
||||
|
||||
while (resultCode == Errors.SQLITE_ROW || resultCode == Errors.SQLITE_OK) {
|
||||
resultCode = bindings.sqlite3_step(statement);
|
||||
|
@ -82,13 +84,13 @@ class Database {
|
|||
|
||||
/// Evaluate a query and return the resulting rows as an iterable.
|
||||
Result query(String query) {
|
||||
Pointer<Pointer<Statement>> statementOut = allocate();
|
||||
Pointer<Pointer<Statement>> statementOut = calloc();
|
||||
Pointer<Utf8> queryC = Utf8.toUtf8(query);
|
||||
int resultCode = bindings.sqlite3_prepare_v2(
|
||||
_database, queryC, -1, statementOut, nullptr);
|
||||
Pointer<Statement> statement = statementOut.value;
|
||||
free(statementOut);
|
||||
free(queryC);
|
||||
calloc.free(statementOut);
|
||||
calloc.free(queryC);
|
||||
|
||||
if (resultCode != Errors.SQLITE_OK) {
|
||||
bindings.sqlite3_finalize(statement);
|
||||
|
|
|
@ -7,6 +7,8 @@ import "dart:ffi";
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
/// [Arena] manages allocated C memory.
|
||||
///
|
||||
/// Arenas are zoned.
|
||||
|
@ -24,7 +26,7 @@ class Arena {
|
|||
/// Frees all memory pointed to by [Pointer]s in this arena.
|
||||
void finalize() {
|
||||
for (final ptr in _allocations) {
|
||||
free(ptr);
|
||||
calloc.free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
109
samples/ffi/sqlite/lib/src/ffi/calloc.dart
Normal file
109
samples/ffi/sqlite/lib/src/ffi/calloc.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be
|
||||
// rolled. We need to wait until the `Allocator` interface has rolled into
|
||||
// Flutter.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary stdlib = Platform.isWindows
|
||||
? DynamicLibrary.open('kernel32.dll')
|
||||
: DynamicLibrary.process();
|
||||
|
||||
typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size);
|
||||
typedef PosixCalloc = Pointer Function(int num, int size);
|
||||
final PosixCalloc posixCalloc =
|
||||
stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
|
||||
|
||||
typedef WinHeapFreeNative = Int32 Function(
|
||||
Pointer heap, Uint32 flags, Pointer memory);
|
||||
typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
|
||||
final WinHeapFree winHeapFree =
|
||||
stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
|
||||
|
||||
const int HEAP_ZERO_MEMORY = 8;
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
class _CallocAllocator implements Allocator {
|
||||
const _CallocAllocator();
|
||||
|
||||
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
|
||||
/// heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc`. On Windows, it uses
|
||||
/// `HeapAlloc` against the default public heap.
|
||||
///
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
|
||||
Pointer<T> result;
|
||||
if (Platform.isWindows) {
|
||||
result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
|
||||
.cast();
|
||||
} else {
|
||||
result = posixCalloc(byteCount, 1).cast();
|
||||
}
|
||||
if (result.address == 0) {
|
||||
throw ArgumentError('Could not allocate $byteCount bytes.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Releases memory allocated on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
|
||||
/// freed.
|
||||
///
|
||||
// TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead
|
||||
// of testing the return integer to be non-zero.
|
||||
@override
|
||||
void free(Pointer pointer) {
|
||||
if (Platform.isWindows) {
|
||||
if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
|
||||
throw ArgumentError('Could not free $pointer.');
|
||||
}
|
||||
} else {
|
||||
posixFree(pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero. Use [malloc] for unintialized
|
||||
/// memory allocation.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
const Allocator calloc = _CallocAllocator();
|
|
@ -168,6 +168,6 @@ void main() {
|
|||
final String test = 'Hasta Mañana';
|
||||
final medium = Utf8.toUtf8(test);
|
||||
expect(test, medium.ref.toString());
|
||||
free(medium);
|
||||
calloc.free(medium);
|
||||
});
|
||||
}
|
||||
|
|
111
samples_2/ffi/calloc.dart
Normal file
111
samples_2/ffi/calloc.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be
|
||||
// rolled. We need to wait until the `Allocator` interface has rolled into
|
||||
// Flutter.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary stdlib = Platform.isWindows
|
||||
? DynamicLibrary.open('kernel32.dll')
|
||||
: DynamicLibrary.process();
|
||||
|
||||
typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size);
|
||||
typedef PosixCalloc = Pointer Function(int num, int size);
|
||||
final PosixCalloc posixCalloc =
|
||||
stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
|
||||
|
||||
typedef WinHeapFreeNative = Int32 Function(
|
||||
Pointer heap, Uint32 flags, Pointer memory);
|
||||
typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
|
||||
final WinHeapFree winHeapFree =
|
||||
stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
|
||||
|
||||
const int HEAP_ZERO_MEMORY = 8;
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
class _CallocAllocator implements Allocator {
|
||||
const _CallocAllocator();
|
||||
|
||||
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
|
||||
/// heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc`. On Windows, it uses
|
||||
/// `HeapAlloc` against the default public heap.
|
||||
///
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> result;
|
||||
if (Platform.isWindows) {
|
||||
result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
|
||||
.cast();
|
||||
} else {
|
||||
result = posixCalloc(byteCount, 1).cast();
|
||||
}
|
||||
if (result.address == 0) {
|
||||
throw ArgumentError('Could not allocate $byteCount bytes.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Releases memory allocated on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
|
||||
/// freed.
|
||||
///
|
||||
// TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead
|
||||
// of testing the return integer to be non-zero.
|
||||
@override
|
||||
void free(Pointer pointer) {
|
||||
if (Platform.isWindows) {
|
||||
if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
|
||||
throw ArgumentError('Could not free $pointer.');
|
||||
}
|
||||
} else {
|
||||
posixFree(pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero. Use [malloc] for unintialized
|
||||
/// memory allocation.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
const Allocator calloc = _CallocAllocator();
|
|
@ -18,8 +18,9 @@ class Coordinate extends Struct {
|
|||
|
||||
Pointer<Coordinate> next;
|
||||
|
||||
factory Coordinate.allocate(double x, double y, Pointer<Coordinate> next) {
|
||||
return allocate<Coordinate>().ref
|
||||
factory Coordinate.allocate(
|
||||
Allocator allocator, double x, double y, Pointer<Coordinate> next) {
|
||||
return allocator<Coordinate>().ref
|
||||
..x = x
|
||||
..y = y
|
||||
..next = next;
|
||||
|
|
|
@ -7,48 +7,36 @@
|
|||
// @dart = 2.9
|
||||
|
||||
import "dart:async";
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart' as packageFfi;
|
||||
import 'package:ffi/ffi.dart' show Utf8;
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
/// Manages native resources.
|
||||
import '../calloc.dart';
|
||||
|
||||
/// Keeps track of all allocated memory and frees all allocated memory on
|
||||
/// [releaseAll].
|
||||
///
|
||||
/// Primary implementations are [Pool] and [Unmanaged].
|
||||
abstract class ResourceManager {
|
||||
/// Allocates memory on the native heap.
|
||||
///
|
||||
/// The native memory is under management by this [ResourceManager].
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count: 1});
|
||||
}
|
||||
/// Wraps an [Allocator] to do the actual allocation and freeing.
|
||||
class Pool implements Allocator {
|
||||
/// The [Allocator] used for allocation and freeing.
|
||||
final Allocator _wrappedAllocator;
|
||||
|
||||
Pool(this._wrappedAllocator);
|
||||
|
||||
/// Manages native resources.
|
||||
class Pool implements ResourceManager {
|
||||
/// Native memory under management by this [Pool].
|
||||
final List<Pointer<NativeType>> _managedMemoryPointers = [];
|
||||
|
||||
/// Callbacks for releasing native resources under management by this [Pool].
|
||||
final List<Function()> _managedResourceReleaseCallbacks = [];
|
||||
|
||||
/// Allocates memory on the native heap.
|
||||
/// Allocates memory on the native heap by using the allocator supplied to
|
||||
/// the constructor.
|
||||
///
|
||||
/// The native memory is under management by this [Pool].
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count: 1}) {
|
||||
final p = Unmanaged().allocate<T>(count: count);
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int numBytes, {int alignment}) {
|
||||
final p = _wrappedAllocator.allocate<T>(numBytes, alignment: alignment);
|
||||
_managedMemoryPointers.add(p);
|
||||
return p;
|
||||
}
|
||||
|
@ -73,17 +61,21 @@ class Pool implements ResourceManager {
|
|||
}
|
||||
_managedResourceReleaseCallbacks.clear();
|
||||
for (final p in _managedMemoryPointers) {
|
||||
Unmanaged().free(p);
|
||||
_wrappedAllocator.free(p);
|
||||
}
|
||||
_managedMemoryPointers.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void free(Pointer<NativeType> pointer) => throw UnsupportedError(
|
||||
"Individually freeing Pool allocated memory is not allowed");
|
||||
}
|
||||
|
||||
/// Creates a [Pool] to manage native resources.
|
||||
///
|
||||
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
|
||||
R using<R>(R Function(Pool) f) {
|
||||
final p = Pool();
|
||||
R using<R>(R Function(Pool) f, [Allocator wrappedAllocator = calloc]) {
|
||||
final p = Pool(wrappedAllocator);
|
||||
try {
|
||||
return f(p);
|
||||
} finally {
|
||||
|
@ -98,8 +90,8 @@ R using<R>(R Function(Pool) f) {
|
|||
/// Please note that all throws are caught and packaged in [RethrownError].
|
||||
///
|
||||
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
|
||||
R usePool<R>(R Function() f) {
|
||||
final p = Pool();
|
||||
R usePool<R>(R Function() f, [Allocator wrappedAllocator = calloc]) {
|
||||
final p = Pool(wrappedAllocator);
|
||||
try {
|
||||
return runZoned(() => f(),
|
||||
zoneValues: {#_pool: p},
|
||||
|
@ -119,78 +111,3 @@ class RethrownError {
|
|||
toString() => """RethrownError(${original})
|
||||
${originalStackTrace}""";
|
||||
}
|
||||
|
||||
/// Does not manage it's resources.
|
||||
class Unmanaged implements ResourceManager {
|
||||
/// Allocates memory on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses malloc. On Windows, it uses HeapAlloc
|
||||
/// against the default public heap. Allocation of either element size or count
|
||||
/// of 0 is undefined.
|
||||
///
|
||||
/// Throws an ArgumentError on failure to allocate.
|
||||
Pointer<T> allocate<T extends NativeType>({int count = 1}) =>
|
||||
packageFfi.allocate(count: count);
|
||||
|
||||
/// Releases memory on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses free. On Windows, it uses HeapFree
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an ArgumentError on failure to free.
|
||||
///
|
||||
void free(Pointer pointer) => packageFfi.free(pointer);
|
||||
}
|
||||
|
||||
/// Does not manage it's resources.
|
||||
final Unmanaged unmanaged = Unmanaged();
|
||||
|
||||
extension Utf8InPool on String {
|
||||
/// Convert a [String] to a Utf8-encoded null-terminated C string.
|
||||
///
|
||||
/// If 'string' contains NULL bytes, the converted string will be truncated
|
||||
/// prematurely. Unpaired surrogate code points in [string] will be preserved
|
||||
/// in the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
|
||||
///
|
||||
/// Returns a malloc-allocated pointer to the result.
|
||||
///
|
||||
/// The memory is managed by the [Pool] passed in as [pool].
|
||||
Pointer<Utf8> toUtf8(ResourceManager pool) {
|
||||
final units = utf8.encode(this);
|
||||
final Pointer<Uint8> result = pool.allocate<Uint8>(count: units.length + 1);
|
||||
final Uint8List nativeString = result.asTypedList(units.length + 1);
|
||||
nativeString.setAll(0, units);
|
||||
nativeString[units.length] = 0;
|
||||
return result.cast();
|
||||
}
|
||||
}
|
||||
|
||||
extension Utf8Helpers on Pointer<Utf8> {
|
||||
/// Returns the length of a null-terminated string -- the number of (one-byte)
|
||||
/// characters before the first null byte.
|
||||
int strlen() {
|
||||
final Pointer<Uint8> array = this.cast<Uint8>();
|
||||
final Uint8List nativeString = array.asTypedList(_maxSize);
|
||||
return nativeString.indexWhere((char) => char == 0);
|
||||
}
|
||||
|
||||
/// Creates a [String] containing the characters UTF-8 encoded in [this].
|
||||
///
|
||||
/// [this] must be a zero-terminated byte sequence of valid UTF-8
|
||||
/// encodings of Unicode code points. It may also contain UTF-8 encodings of
|
||||
/// unpaired surrogate code points, which is not otherwise valid UTF-8, but
|
||||
/// which may be created when encoding a Dart string containing an unpaired
|
||||
/// surrogate. See [Utf8Decoder] for details on decoding.
|
||||
///
|
||||
/// Returns a Dart string containing the decoded code points.
|
||||
String contents() {
|
||||
final int length = strlen();
|
||||
return utf8.decode(Uint8List.view(
|
||||
this.cast<Uint8>().asTypedList(length).buffer, 0, length));
|
||||
}
|
||||
}
|
||||
|
||||
const int _kMaxSmi64 = (1 << 62) - 1;
|
||||
const int _kMaxSmi32 = (1 << 30) - 1;
|
||||
final int _maxSize = sizeOf<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'dart:ffi';
|
|||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
|
@ -23,7 +24,7 @@ main() {
|
|||
|
||||
// To ensure resources are freed, wrap them in a [using] call.
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
|
@ -33,14 +34,14 @@ main() {
|
|||
// Resources are freed also when abnormal control flow occurs.
|
||||
try {
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
p[0] = 25;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
|
||||
print(p[1]);
|
||||
Expect.equals(25, p[1]);
|
||||
throw Exception("Some random exception");
|
||||
});
|
||||
// `free(p)` has been called.
|
||||
// `calloc.free(p)` has been called.
|
||||
} on Exception catch (e) {
|
||||
print("Caught exception: $e");
|
||||
}
|
||||
|
@ -48,8 +49,8 @@ main() {
|
|||
// In a pool multiple resources can be allocated, which will all be freed
|
||||
// at the end of the scope.
|
||||
using((Pool pool) {
|
||||
final p = pool.allocate<Int64>(count: 2);
|
||||
final p2 = pool.allocate<Int64>(count: 2);
|
||||
final p = pool<Int64>(2);
|
||||
final p2 = pool<Int64>(2);
|
||||
p[0] = 1;
|
||||
p[1] = 2;
|
||||
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
|
||||
|
@ -60,7 +61,7 @@ main() {
|
|||
// If the resource allocation happens in a different scope, then one either
|
||||
// needs to pass the pool to that scope.
|
||||
f1(Pool pool) {
|
||||
return pool.allocate<Int64>(count: 2);
|
||||
return pool<Int64>(2);
|
||||
}
|
||||
|
||||
using((Pool pool) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'dart:ffi';
|
|||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
|
@ -23,7 +24,7 @@ main() {
|
|||
|
||||
// To ensure resources are freed, wrap them in a [using] call.
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
|
@ -33,7 +34,7 @@ main() {
|
|||
// Resources are freed also when abnormal control flow occurs.
|
||||
try {
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
p[0] = 25;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), 8);
|
||||
print(p[1]);
|
||||
|
@ -48,8 +49,8 @@ main() {
|
|||
// In a pool multiple resources can be allocated, which will all be freed
|
||||
// at the end of the scope.
|
||||
usePool(() {
|
||||
final p = currentPool.allocate<Int64>(count: 2);
|
||||
final p2 = currentPool.allocate<Int64>(count: 2);
|
||||
final p = currentPool<Int64>(2);
|
||||
final p2 = currentPool<Int64>(2);
|
||||
p[0] = 1;
|
||||
p[1] = 2;
|
||||
MemMove(p2.cast<Void>(), p.cast<Void>(), 2 * sizeOf<Int64>());
|
||||
|
@ -60,7 +61,7 @@ main() {
|
|||
// If the resource allocation happens in a different scope, it is in the
|
||||
// same zone, so it's lifetime is automatically managed by the pool.
|
||||
f1() {
|
||||
return currentPool.allocate<Int64>(count: 2);
|
||||
return currentPool<Int64>(2);
|
||||
}
|
||||
|
||||
usePool(() {
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'pool.dart';
|
||||
import 'utf8_helpers.dart';
|
||||
import '../calloc.dart';
|
||||
import '../dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
|
@ -24,15 +26,15 @@ main() {
|
|||
// To ensure resources are freed, call free manually.
|
||||
//
|
||||
// For automatic management use a Pool.
|
||||
final p = unmanaged.allocate<Int64>(count: 2);
|
||||
final p = calloc<Int64>(2);
|
||||
p[0] = 24;
|
||||
MemMove(p.elementAt(1).cast<Void>(), p.cast<Void>(), sizeOf<Int64>());
|
||||
print(p[1]);
|
||||
Expect.equals(24, p[1]);
|
||||
unmanaged.free(p);
|
||||
calloc.free(p);
|
||||
|
||||
// Using Strings.
|
||||
final p2 = "Hello world!".toUtf8(unmanaged);
|
||||
final p2 = "Hello world!".toUtf8(calloc);
|
||||
print(p2.contents());
|
||||
unmanaged.free(p2);
|
||||
calloc.free(p2);
|
||||
}
|
||||
|
|
57
samples_2/ffi/resource_management/utf8_helpers.dart
Normal file
57
samples_2/ffi/resource_management/utf8_helpers.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// Explicit pool used for managing resources.
|
||||
|
||||
// @dart = 2.9
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
extension Utf8InPool on String {
|
||||
/// Convert a [String] to a Utf8-encoded null-terminated C string.
|
||||
///
|
||||
/// If 'string' contains NULL bytes, the converted string will be truncated
|
||||
/// prematurely. See [Utf8Encoder] for details on encoding.
|
||||
///
|
||||
/// Returns a [allocator]-allocated pointer to the result.
|
||||
Pointer<Utf8> toUtf8(Allocator allocator) {
|
||||
final units = utf8.encode(this);
|
||||
final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
|
||||
final Uint8List nativeString = result.asTypedList(units.length + 1);
|
||||
nativeString.setAll(0, units);
|
||||
nativeString[units.length] = 0;
|
||||
return result.cast();
|
||||
}
|
||||
}
|
||||
|
||||
extension Utf8Helpers on Pointer<Utf8> {
|
||||
/// The length of a null-terminated string — the number of (one-byte)
|
||||
/// characters before the first null byte.
|
||||
int get strlen {
|
||||
final Pointer<Uint8> array = this.cast<Uint8>();
|
||||
final Uint8List nativeString = array.asTypedList(_maxSize);
|
||||
return nativeString.indexWhere((char) => char == 0);
|
||||
}
|
||||
|
||||
/// Creates a [String] containing the characters UTF-8 encoded in [this].
|
||||
///
|
||||
/// [this] must be a zero-terminated byte sequence of valid UTF-8
|
||||
/// encodings of Unicode code points.See [Utf8Decoder] for details on
|
||||
/// decoding.
|
||||
///
|
||||
/// Returns a Dart string containing the decoded code points.
|
||||
String contents() {
|
||||
final int length = strlen;
|
||||
return utf8.decode(Uint8List.view(
|
||||
this.cast<Uint8>().asTypedList(length).buffer, 0, length));
|
||||
}
|
||||
}
|
||||
|
||||
const int _kMaxSmi64 = (1 << 62) - 1;
|
||||
const int _kMaxSmi32 = (1 << 30) - 1;
|
||||
final int _maxSize = sizeOf<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
|
|
@ -9,6 +9,8 @@ import 'dart:ffi';
|
|||
import 'package:ffi/ffi.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
/// typedef struct {
|
||||
/// unsigned int bold : 1;
|
||||
/// unsigned int underline : 2;
|
||||
|
@ -95,7 +97,7 @@ extension IntBitField on int {
|
|||
}
|
||||
|
||||
main() {
|
||||
final p = allocate<ScreenCellAttrs>(count: 3);
|
||||
final p = calloc<ScreenCellAttrs>(3);
|
||||
|
||||
// Zeroes out all fields.
|
||||
p.ref.bits = 0;
|
||||
|
@ -121,5 +123,5 @@ main() {
|
|||
// A check for automated testing.
|
||||
Expect.equals(1933, p.ref.bits);
|
||||
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
|
|
@ -7,29 +7,31 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
main() {
|
||||
print('start main');
|
||||
|
||||
{
|
||||
// Basic operation: allocate, get, set, and free.
|
||||
Pointer<Int64> p = allocate();
|
||||
Pointer<Int64> p = calloc();
|
||||
p.value = 42;
|
||||
int pValue = p.value;
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Undefined behavior before set.
|
||||
Pointer<Int64> p = allocate();
|
||||
Pointer<Int64> p = calloc();
|
||||
int pValue = p.value;
|
||||
print('If not set, returns garbage: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Pointers can be created from an address.
|
||||
Pointer<Int64> pHelper = allocate();
|
||||
Pointer<Int64> pHelper = calloc();
|
||||
pHelper.value = 1337;
|
||||
|
||||
int address = pHelper.address;
|
||||
|
@ -38,13 +40,13 @@ main() {
|
|||
Pointer<Int64> p = Pointer.fromAddress(address);
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
|
||||
free(pHelper);
|
||||
calloc.free(pHelper);
|
||||
}
|
||||
|
||||
{
|
||||
// Address is zeroed out after free.
|
||||
Pointer<Int64> p = allocate();
|
||||
free(p);
|
||||
Pointer<Int64> p = calloc();
|
||||
calloc.free(p);
|
||||
print('After free, address is zero: ${p.address}');
|
||||
}
|
||||
|
||||
|
@ -52,13 +54,13 @@ main() {
|
|||
// Allocating too much throws an exception.
|
||||
try {
|
||||
int maxMint = 9223372036854775807; // 2^63 - 1
|
||||
allocate<Int64>(count: maxMint);
|
||||
calloc<Int64>(maxMint);
|
||||
} on Error {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
try {
|
||||
int maxInt1_8 = 1152921504606846975; // 2^60 -1
|
||||
allocate<Int64>(count: maxInt1_8);
|
||||
calloc<Int64>(maxInt1_8);
|
||||
} on Error {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
|
@ -67,7 +69,7 @@ main() {
|
|||
{
|
||||
// Pointers can be cast into another type,
|
||||
// resulting in the corresponding bits read.
|
||||
Pointer<Int64> p1 = allocate();
|
||||
Pointer<Int64> p1 = calloc();
|
||||
p1.value = 9223372036854775807; // 2^63 - 1
|
||||
|
||||
Pointer<Int32> p2 = p1.cast();
|
||||
|
@ -76,61 +78,61 @@ main() {
|
|||
Pointer<Int32> p3 = p2.elementAt(1);
|
||||
print('${p3.runtimeType} value: ${p3.value}'); // 2^31 - 1
|
||||
|
||||
free(p1);
|
||||
calloc.free(p1);
|
||||
}
|
||||
|
||||
{
|
||||
// Data can be tightly packed in memory.
|
||||
Pointer<Int8> p = allocate(count: 8);
|
||||
Pointer<Int8> p = calloc(8);
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
p.elementAt(i).value = i * 3;
|
||||
}
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
print('p.elementAt($i) value: ${p.elementAt(i).value}');
|
||||
}
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Values that don't fit are truncated.
|
||||
Pointer<Int32> p11 = allocate();
|
||||
Pointer<Int32> p11 = calloc();
|
||||
|
||||
p11.value = 9223372036854775807;
|
||||
|
||||
print(p11);
|
||||
|
||||
free(p11);
|
||||
calloc.free(p11);
|
||||
}
|
||||
|
||||
{
|
||||
// Doubles.
|
||||
Pointer<Double> p = allocate();
|
||||
Pointer<Double> p = calloc();
|
||||
p.value = 3.14159265359;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
p.value = 3.14;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Floats.
|
||||
Pointer<Float> p = allocate();
|
||||
Pointer<Float> p = calloc();
|
||||
p.value = 3.14159265359;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
p.value = 3.14;
|
||||
print('${p.runtimeType} value: ${p.value}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// IntPtr varies in size based on whether the platform is 32 or 64 bit.
|
||||
// Addresses of pointers fit in this size.
|
||||
Pointer<IntPtr> p = allocate();
|
||||
Pointer<IntPtr> p = calloc();
|
||||
int p14addr = p.address;
|
||||
p.value = p14addr;
|
||||
int pValue = p.value;
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
free(p);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -138,19 +140,19 @@ main() {
|
|||
// The size of the element it is pointing to is undefined,
|
||||
// they cannot be allocated, read, or written.
|
||||
|
||||
Pointer<IntPtr> p1 = allocate();
|
||||
Pointer<IntPtr> p1 = calloc();
|
||||
Pointer<Void> p2 = p1.cast();
|
||||
print('${p2.runtimeType} address: ${p2.address}');
|
||||
|
||||
free(p1);
|
||||
calloc.free(p1);
|
||||
}
|
||||
|
||||
{
|
||||
// Pointer to a pointer to something.
|
||||
Pointer<Int16> pHelper = allocate();
|
||||
Pointer<Int16> pHelper = calloc();
|
||||
pHelper.value = 17;
|
||||
|
||||
Pointer<Pointer<Int16>> p = allocate();
|
||||
Pointer<Pointer<Int16>> p = calloc();
|
||||
|
||||
// Storing into a pointer pointer automatically unboxes.
|
||||
p.value = pHelper;
|
||||
|
@ -162,31 +164,31 @@ main() {
|
|||
int pValue = p.value.value;
|
||||
print('${p.runtimeType} value\'s value: ${pValue}');
|
||||
|
||||
free(p);
|
||||
free(pHelper);
|
||||
calloc.free(p);
|
||||
calloc.free(pHelper);
|
||||
}
|
||||
|
||||
{
|
||||
// The pointer to pointer types must match up.
|
||||
Pointer<Int8> pHelper = allocate();
|
||||
Pointer<Int8> pHelper = calloc();
|
||||
pHelper.value = 123;
|
||||
|
||||
Pointer<Pointer<Int16>> p = allocate();
|
||||
Pointer<Pointer<Int16>> p = calloc();
|
||||
|
||||
// Trying to store `pHelper` into `p.val` would result in a type mismatch.
|
||||
|
||||
free(pHelper);
|
||||
free(p);
|
||||
calloc.free(pHelper);
|
||||
calloc.free(p);
|
||||
}
|
||||
|
||||
{
|
||||
// `nullptr` points to address 0 in c++.
|
||||
Pointer<Pointer<Int8>> pointerToPointer = allocate();
|
||||
Pointer<Pointer<Int8>> pointerToPointer = calloc();
|
||||
Pointer<Int8> value = nullptr;
|
||||
pointerToPointer.value = value;
|
||||
value = pointerToPointer.value;
|
||||
print("Loading a pointer to the 0 address is null: ${value}");
|
||||
free(pointerToPointer);
|
||||
calloc.free(pointerToPointer);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -207,7 +209,7 @@ main() {
|
|||
head.value = value;
|
||||
return;
|
||||
}
|
||||
Pointer<IntPtr> next = allocate<IntPtr>();
|
||||
Pointer<IntPtr> next = calloc<IntPtr>();
|
||||
head.value = next.address;
|
||||
createChain(next, length - 1, value);
|
||||
}
|
||||
|
@ -222,7 +224,7 @@ main() {
|
|||
|
||||
void freeChain(Pointer<IntPtr> head, int length) {
|
||||
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
|
||||
free(head);
|
||||
calloc.free(head);
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -230,7 +232,7 @@ main() {
|
|||
}
|
||||
|
||||
int length = 10;
|
||||
Pointer<IntPtr> head = allocate();
|
||||
Pointer<IntPtr> head = calloc();
|
||||
createChain(head, length, 512);
|
||||
int tailValue = getChainValue(head, length);
|
||||
print('tailValue: ${tailValue}');
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:ffi';
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
typedef NativeUnaryOp = Int32 Function(Int32);
|
||||
|
@ -186,7 +187,7 @@ main() {
|
|||
// pass an array / pointer as argument
|
||||
Int64PointerUnOp assign1337Index1 = ffiTestFunctions
|
||||
.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
|
||||
Pointer<Int64> p2 = allocate(count: 2);
|
||||
Pointer<Int64> p2 = calloc(2);
|
||||
p2.value = 42;
|
||||
p2[1] = 1000;
|
||||
print(p2.elementAt(1).address.toRadixString(16));
|
||||
|
@ -251,11 +252,11 @@ main() {
|
|||
print(result);
|
||||
print(result.runtimeType);
|
||||
|
||||
Pointer<Int64> p2 = allocate(count: 2);
|
||||
Pointer<Int64> p2 = calloc(2);
|
||||
result = nullableInt64ElemAt1(p2);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
free(p2);
|
||||
calloc.free(p2);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
|
@ -45,7 +48,7 @@ main() {
|
|||
Pointer<NativeFunction<CoordinateTrice>> p2 =
|
||||
ffiTestFunctions.lookup("CoordinateUnOpTrice");
|
||||
CoordinateTrice coordinateUnOpTrice = p2.asFunction();
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 20.0, nullptr);
|
||||
c1.next = c1.addressOf;
|
||||
Coordinate result =
|
||||
coordinateUnOpTrice(transposeCoordinatePointer, c1.addressOf).ref;
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Pointer<Coordinate> Function(Pointer<Coordinate>);
|
||||
|
||||
main() {
|
||||
|
@ -25,8 +26,8 @@ main() {
|
|||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 20.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(42.0, 84.0, c1.addressOf);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 20.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(calloc, 42.0, 84.0, c1.addressOf);
|
||||
c1.next = c2.addressOf;
|
||||
|
||||
Coordinate result = f1(c1.addressOf).ref;
|
||||
|
@ -46,7 +47,7 @@ main() {
|
|||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
|
||||
Pointer<Coordinate> c1 = calloc<Coordinate>(3);
|
||||
Pointer<Coordinate> c2 = c1.elementAt(1);
|
||||
Pointer<Coordinate> c3 = c1.elementAt(2);
|
||||
c1.ref.x = 10.0;
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:ffi';
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
import 'coordinate.dart';
|
||||
|
||||
main() {
|
||||
|
@ -15,9 +16,9 @@ main() {
|
|||
|
||||
{
|
||||
// Allocates each coordinate separately in c memory.
|
||||
Coordinate c1 = Coordinate.allocate(10.0, 10.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(20.0, 20.0, c1.addressOf);
|
||||
Coordinate c3 = Coordinate.allocate(30.0, 30.0, c2.addressOf);
|
||||
Coordinate c1 = Coordinate.allocate(calloc, 10.0, 10.0, nullptr);
|
||||
Coordinate c2 = Coordinate.allocate(calloc, 20.0, 20.0, c1.addressOf);
|
||||
Coordinate c3 = Coordinate.allocate(calloc, 30.0, 30.0, c2.addressOf);
|
||||
c1.next = c3.addressOf;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
|
@ -26,14 +27,14 @@ main() {
|
|||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
free(c1.addressOf);
|
||||
free(c2.addressOf);
|
||||
free(c3.addressOf);
|
||||
calloc.free(c1.addressOf);
|
||||
calloc.free(c2.addressOf);
|
||||
calloc.free(c3.addressOf);
|
||||
}
|
||||
|
||||
{
|
||||
// Allocates coordinates consecutively in c memory.
|
||||
Pointer<Coordinate> c1 = allocate<Coordinate>(count: 3);
|
||||
Pointer<Coordinate> c1 = calloc<Coordinate>(3);
|
||||
Pointer<Coordinate> c2 = c1.elementAt(1);
|
||||
Pointer<Coordinate> c3 = c1.elementAt(2);
|
||||
c1.ref.x = 10.0;
|
||||
|
@ -52,15 +53,15 @@ main() {
|
|||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
free(c1);
|
||||
calloc.free(c1);
|
||||
}
|
||||
|
||||
{
|
||||
Coordinate c = Coordinate.allocate(10, 10, nullptr);
|
||||
Coordinate c = Coordinate.allocate(calloc, 10, 10, nullptr);
|
||||
print(c is Coordinate);
|
||||
print(c is Pointer<Void>);
|
||||
print(c is Pointer);
|
||||
free(c.addressOf);
|
||||
calloc.free(c.addressOf);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
|
|
|
@ -10,3 +10,5 @@
|
|||
library sqlite;
|
||||
|
||||
export "src/database.dart";
|
||||
|
||||
export "src/ffi/calloc.dart" show calloc;
|
||||
|
|
|
@ -17,6 +17,8 @@ import "bindings/types.dart" hide Database;
|
|||
import "bindings/constants.dart";
|
||||
import "collections/closable_iterator.dart";
|
||||
|
||||
import 'ffi/calloc.dart';
|
||||
|
||||
/// [Database] represents an open connection to a SQLite database.
|
||||
///
|
||||
/// All functions against a database may throw [SQLiteError].
|
||||
|
@ -29,13 +31,13 @@ class Database {
|
|||
/// Open a database located at the file [path].
|
||||
Database(String path,
|
||||
[int flags = Flags.SQLITE_OPEN_READWRITE | Flags.SQLITE_OPEN_CREATE]) {
|
||||
Pointer<Pointer<types.Database>> dbOut = allocate();
|
||||
Pointer<Pointer<types.Database>> dbOut = calloc();
|
||||
final pathC = Utf8.toUtf8(path);
|
||||
final int resultCode =
|
||||
bindings.sqlite3_open_v2(pathC, dbOut, flags, nullptr);
|
||||
_database = dbOut.value;
|
||||
free(dbOut);
|
||||
free(pathC);
|
||||
calloc.free(dbOut);
|
||||
calloc.free(pathC);
|
||||
|
||||
if (resultCode == Errors.SQLITE_OK) {
|
||||
_open = true;
|
||||
|
@ -65,13 +67,13 @@ class Database {
|
|||
|
||||
/// Execute a query, discarding any returned rows.
|
||||
void execute(String query) {
|
||||
Pointer<Pointer<Statement>> statementOut = allocate();
|
||||
Pointer<Pointer<Statement>> statementOut = calloc();
|
||||
Pointer<Utf8> queryC = Utf8.toUtf8(query);
|
||||
int resultCode = bindings.sqlite3_prepare_v2(
|
||||
_database, queryC, -1, statementOut, nullptr);
|
||||
Pointer<Statement> statement = statementOut.value;
|
||||
free(statementOut);
|
||||
free(queryC);
|
||||
calloc.free(statementOut);
|
||||
calloc.free(queryC);
|
||||
|
||||
while (resultCode == Errors.SQLITE_ROW || resultCode == Errors.SQLITE_OK) {
|
||||
resultCode = bindings.sqlite3_step(statement);
|
||||
|
@ -84,13 +86,13 @@ class Database {
|
|||
|
||||
/// Evaluate a query and return the resulting rows as an iterable.
|
||||
Result query(String query) {
|
||||
Pointer<Pointer<Statement>> statementOut = allocate();
|
||||
Pointer<Pointer<Statement>> statementOut = calloc();
|
||||
Pointer<Utf8> queryC = Utf8.toUtf8(query);
|
||||
int resultCode = bindings.sqlite3_prepare_v2(
|
||||
_database, queryC, -1, statementOut, nullptr);
|
||||
Pointer<Statement> statement = statementOut.value;
|
||||
free(statementOut);
|
||||
free(queryC);
|
||||
calloc.free(statementOut);
|
||||
calloc.free(queryC);
|
||||
|
||||
if (resultCode != Errors.SQLITE_OK) {
|
||||
bindings.sqlite3_finalize(statement);
|
||||
|
|
|
@ -9,6 +9,8 @@ import "dart:ffi";
|
|||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'calloc.dart';
|
||||
|
||||
/// [Arena] manages allocated C memory.
|
||||
///
|
||||
/// Arenas are zoned.
|
||||
|
@ -26,7 +28,7 @@ class Arena {
|
|||
/// Frees all memory pointed to by [Pointer]s in this arena.
|
||||
void finalize() {
|
||||
for (final ptr in _allocations) {
|
||||
free(ptr);
|
||||
calloc.free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
111
samples_2/ffi/sqlite/lib/src/ffi/calloc.dart
Normal file
111
samples_2/ffi/sqlite/lib/src/ffi/calloc.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be
|
||||
// rolled. We need to wait until the `Allocator` interface has rolled into
|
||||
// Flutter.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary stdlib = Platform.isWindows
|
||||
? DynamicLibrary.open('kernel32.dll')
|
||||
: DynamicLibrary.process();
|
||||
|
||||
typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size);
|
||||
typedef PosixCalloc = Pointer Function(int num, int size);
|
||||
final PosixCalloc posixCalloc =
|
||||
stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
|
||||
|
||||
typedef PosixFreeNative = Void Function(Pointer);
|
||||
typedef PosixFree = void Function(Pointer);
|
||||
final PosixFree posixFree =
|
||||
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
|
||||
|
||||
typedef WinGetProcessHeapFn = Pointer Function();
|
||||
final WinGetProcessHeapFn winGetProcessHeap = stdlib
|
||||
.lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
|
||||
final Pointer processHeap = winGetProcessHeap();
|
||||
|
||||
typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
|
||||
typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
|
||||
final WinHeapAlloc winHeapAlloc =
|
||||
stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
|
||||
|
||||
typedef WinHeapFreeNative = Int32 Function(
|
||||
Pointer heap, Uint32 flags, Pointer memory);
|
||||
typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
|
||||
final WinHeapFree winHeapFree =
|
||||
stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
|
||||
|
||||
const int HEAP_ZERO_MEMORY = 8;
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
class _CallocAllocator implements Allocator {
|
||||
const _CallocAllocator();
|
||||
|
||||
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
|
||||
/// heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc`. On Windows, it uses
|
||||
/// `HeapAlloc` against the default public heap.
|
||||
///
|
||||
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
|
||||
/// satisfied.
|
||||
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
|
||||
@override
|
||||
Pointer<T> allocate<T extends NativeType>(int byteCount, {int alignment}) {
|
||||
Pointer<T> result;
|
||||
if (Platform.isWindows) {
|
||||
result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
|
||||
.cast();
|
||||
} else {
|
||||
result = posixCalloc(byteCount, 1).cast();
|
||||
}
|
||||
if (result.address == 0) {
|
||||
throw ArgumentError('Could not allocate $byteCount bytes.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Releases memory allocated on the native heap.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
|
||||
/// against the default public heap. It may only be used against pointers
|
||||
/// allocated in a manner equivalent to [allocate].
|
||||
///
|
||||
/// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
|
||||
/// freed.
|
||||
///
|
||||
// TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead
|
||||
// of testing the return integer to be non-zero.
|
||||
@override
|
||||
void free(Pointer pointer) {
|
||||
if (Platform.isWindows) {
|
||||
if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
|
||||
throw ArgumentError('Could not free $pointer.');
|
||||
}
|
||||
} else {
|
||||
posixFree(pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages memory on the native heap.
|
||||
///
|
||||
/// Initializes newly allocated memory to zero. Use [malloc] for unintialized
|
||||
/// memory allocation.
|
||||
///
|
||||
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
|
||||
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
|
||||
/// public heap.
|
||||
const Allocator calloc = _CallocAllocator();
|
|
@ -172,6 +172,6 @@ void main() {
|
|||
final String test = 'Hasta Mañana';
|
||||
final medium = Utf8.toUtf8(test);
|
||||
expect(test, medium.ref.toString());
|
||||
free(medium);
|
||||
calloc.free(medium);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue