Skip to content

Commit 6bd0935

Browse files
authored
[ffi] Expose pointer to free from allocators (dart-archive/ffi#203)
1 parent 7db243c commit 6bd0935

File tree

7 files changed

+98
-19
lines changed

7 files changed

+98
-19
lines changed

.github/workflows/ffi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
matrix:
4848
# Add macos-latest and/or windows-latest if relevant for this package.
4949
os: [ubuntu-latest]
50-
sdk: [2.17.0, dev]
50+
sdk: [3.0.0, dev]
5151
steps:
5252
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
5353
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f

pkgs/ffi/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 2.1.0
2+
3+
- Require Dart 3.0.0 or greater.
4+
- Expose native equivalent to `free` (`nativeFree`) from `malloc` and
5+
`calloc` allocators.
6+
17
## 2.0.2
28

39
- Fixed a typo in a doc comment.

pkgs/ffi/lib/src/allocation.dart

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ final PosixCalloc posixCalloc =
2222

2323
typedef PosixFreeNative = Void Function(Pointer);
2424
typedef PosixFree = void Function(Pointer);
25-
final PosixFree posixFree =
26-
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
25+
final Pointer<NativeFunction<PosixFreeNative>> posixFreePointer =
26+
stdlib.lookup('free');
27+
final PosixFree posixFree = posixFreePointer.asFunction();
2728

28-
typedef WinCoTaskMemAllocNative = Pointer Function(Size cb);
29-
typedef WinCoTaskMemAlloc = Pointer Function(int cb);
29+
typedef WinCoTaskMemAllocNative = Pointer Function(Size);
30+
typedef WinCoTaskMemAlloc = Pointer Function(int);
3031
final WinCoTaskMemAlloc winCoTaskMemAlloc =
3132
stdlib.lookupFunction<WinCoTaskMemAllocNative, WinCoTaskMemAlloc>(
3233
'CoTaskMemAlloc');
3334

34-
typedef WinCoTaskMemFreeNative = Void Function(Pointer pv);
35-
typedef WinCoTaskMemFree = void Function(Pointer pv);
36-
final WinCoTaskMemFree winCoTaskMemFree = stdlib
37-
.lookupFunction<WinCoTaskMemFreeNative, WinCoTaskMemFree>('CoTaskMemFree');
35+
typedef WinCoTaskMemFreeNative = Void Function(Pointer);
36+
typedef WinCoTaskMemFree = void Function(Pointer);
37+
final Pointer<NativeFunction<WinCoTaskMemFreeNative>> winCoTaskMemFreePointer =
38+
stdlib.lookup('CoTaskMemFree');
39+
final WinCoTaskMemFree winCoTaskMemFree = winCoTaskMemFreePointer.asFunction();
3840

3941
/// Manages memory on the native heap.
4042
///
@@ -43,8 +45,8 @@ final WinCoTaskMemFree winCoTaskMemFree = stdlib
4345
///
4446
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
4547
/// `CoTaskMemAlloc`.
46-
class _MallocAllocator implements Allocator {
47-
const _MallocAllocator();
48+
final class MallocAllocator implements Allocator {
49+
const MallocAllocator._();
4850

4951
/// Allocates [byteCount] bytes of of unitialized memory on the native heap.
5052
///
@@ -81,6 +83,37 @@ class _MallocAllocator implements Allocator {
8183
posixFree(pointer);
8284
}
8385
}
86+
87+
/// Returns a pointer to a native free function.
88+
///
89+
/// This function can be used to release memory allocated by [allocated]
90+
/// from the native side. It can also be used as a finalization callback
91+
/// passed to `NativeFinalizer` constructor or `Pointer.atTypedList`
92+
/// method.
93+
///
94+
/// For example to automatically free native memory when the Dart object
95+
/// wrapping it is reclaimed by GC:
96+
///
97+
/// ```dart
98+
/// class Wrapper implements Finalizable {
99+
/// static final finalizer = NativeFinalizer(malloc.nativeFree);
100+
///
101+
/// final Pointer<Uint8> data;
102+
///
103+
/// Wrapper() : data = malloc.allocate<Uint8>(length) {
104+
/// finalizer.attach(this, data);
105+
/// }
106+
/// }
107+
/// ```
108+
///
109+
/// or to free native memory that is owned by a typed list:
110+
///
111+
/// ```dart
112+
/// malloc.allocate<Uint8>(n).asTypedList(n, finalizer: malloc.nativeFree)
113+
/// ```
114+
///
115+
Pointer<NativeFinalizerFunction> get nativeFree =>
116+
Platform.isWindows ? winCoTaskMemFreePointer : posixFreePointer;
84117
}
85118

86119
/// Manages memory on the native heap.
@@ -90,16 +123,16 @@ class _MallocAllocator implements Allocator {
90123
///
91124
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
92125
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
93-
const Allocator malloc = _MallocAllocator();
126+
const MallocAllocator malloc = MallocAllocator._();
94127

95128
/// Manages memory on the native heap.
96129
///
97130
/// Initializes newly allocated memory to zero.
98131
///
99132
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
100133
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
101-
class _CallocAllocator implements Allocator {
102-
const _CallocAllocator();
134+
final class CallocAllocator implements Allocator {
135+
const CallocAllocator._();
103136

104137
/// Fills a block of memory with a specified value.
105138
void _fillMemory(Pointer destination, int length, int fill) {
@@ -153,6 +186,37 @@ class _CallocAllocator implements Allocator {
153186
posixFree(pointer);
154187
}
155188
}
189+
190+
/// Returns a pointer to a native free function.
191+
///
192+
/// This function can be used to release memory allocated by [allocated]
193+
/// from the native side. It can also be used as a finalization callback
194+
/// passed to `NativeFinalizer` constructor or `Pointer.atTypedList`
195+
/// method.
196+
///
197+
/// For example to automatically free native memory when the Dart object
198+
/// wrapping it is reclaimed by GC:
199+
///
200+
/// ```dart
201+
/// class Wrapper implements Finalizable {
202+
/// static final finalizer = NativeFinalizer(calloc.nativeFree);
203+
///
204+
/// final Pointer<Uint8> data;
205+
///
206+
/// Wrapper() : data = calloc.allocate<Uint8>(length) {
207+
/// finalizer.attach(this, data);
208+
/// }
209+
/// }
210+
/// ```
211+
///
212+
/// or to free native memory that is owned by a typed list:
213+
///
214+
/// ```dart
215+
/// calloc.allocate<Uint8>(n).asTypedList(n, finalizer: calloc.nativeFree)
216+
/// ```
217+
///
218+
Pointer<NativeFinalizerFunction> get nativeFree =>
219+
Platform.isWindows ? winCoTaskMemFreePointer : posixFreePointer;
156220
}
157221

158222
/// Manages memory on the native heap.
@@ -162,4 +226,4 @@ class _CallocAllocator implements Allocator {
162226
///
163227
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
164228
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
165-
const Allocator calloc = _CallocAllocator();
229+
const CallocAllocator calloc = CallocAllocator._();

pkgs/ffi/lib/src/utf16.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'package:ffi/ffi.dart';
1313
/// through a `Pointer<Utf16>` representing the entire array. This pointer is
1414
/// the equivalent of a char pointer (`const wchar_t*`) in C code. The
1515
/// individual UTF-16 code units are stored in native byte order.
16-
class Utf16 extends Opaque {}
16+
final class Utf16 extends Opaque {}
1717

1818
/// Extension method for converting a`Pointer<Utf16>` to a [String].
1919
extension Utf16Pointer on Pointer<Utf16> {

pkgs/ffi/lib/src/utf8.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'package:ffi/ffi.dart';
1313
/// The Utf8 type itself has no functionality, it's only intended to be used
1414
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
1515
/// the equivalent of a char pointer (`const char*`) in C code.
16-
class Utf8 extends Opaque {}
16+
final class Utf8 extends Opaque {}
1717

1818
/// Extension method for converting a`Pointer<Utf8>` to a [String].
1919
extension Utf8Pointer on Pointer<Utf8> {

pkgs/ffi/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: ffi
2-
version: 2.0.2
2+
version: 2.1.0
33
description: Utilities for working with Foreign Function Interface (FFI) code.
44
repository: https://github.com/dart-lang/ffi
55

@@ -9,7 +9,7 @@ topics:
99
- codegen
1010

1111
environment:
12-
sdk: '>=2.17.0 <4.0.0'
12+
sdk: '>=3.0.0 <4.0.0'
1313

1414
dev_dependencies:
1515
test: ^1.21.2

pkgs/ffi/test/allocation_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,13 @@ void main() async {
3838
// amount of addressable memory on the system.
3939
expect(() => calloc<Uint8>(-1), throwsA(isA<ArgumentError>()));
4040
});
41+
42+
test('nativeFree', () {
43+
// malloc.nativeFree should be able to free memory allocated by malloc.
44+
final ptr1 = malloc.allocate<Uint8>(1024);
45+
malloc.nativeFree.asFunction<void Function(Pointer<Void>)>()(ptr1.cast());
46+
// calloc.nativeFree should be able to free memory allocated by calloc.
47+
final ptr2 = calloc.allocate<Uint8>(1024);
48+
calloc.nativeFree.asFunction<void Function(Pointer<Void>)>()(ptr2.cast());
49+
});
4150
}

0 commit comments

Comments
 (0)