Skip to content
This repository has been archived by the owner on Jan 17, 2024. It is now read-only.

Commit

Permalink
Expose pointer to free from allocators (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
mraleph committed Aug 8, 2023
1 parent f01dfca commit e2c01a9
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest]
sdk: [2.17.0, dev]
sdk: [3.0.0, dev]
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2.1.0

- Require Dart 3.0.0 or greater.
- Expose native equivalent to `free` (`nativeFree`) from `malloc` and
`calloc` allocators.

## 2.0.2

- Fixed a typo in a doc comment.
Expand Down
92 changes: 78 additions & 14 deletions lib/src/allocation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,21 @@ final PosixCalloc posixCalloc =

typedef PosixFreeNative = Void Function(Pointer);
typedef PosixFree = void Function(Pointer);
final PosixFree posixFree =
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
final Pointer<NativeFunction<PosixFreeNative>> posixFreePointer =
stdlib.lookup('free');
final PosixFree posixFree = posixFreePointer.asFunction();

typedef WinCoTaskMemAllocNative = Pointer Function(Size cb);
typedef WinCoTaskMemAlloc = Pointer Function(int cb);
typedef WinCoTaskMemAllocNative = Pointer Function(Size);
typedef WinCoTaskMemAlloc = Pointer Function(int);
final WinCoTaskMemAlloc winCoTaskMemAlloc =
stdlib.lookupFunction<WinCoTaskMemAllocNative, WinCoTaskMemAlloc>(
'CoTaskMemAlloc');

typedef WinCoTaskMemFreeNative = Void Function(Pointer pv);
typedef WinCoTaskMemFree = void Function(Pointer pv);
final WinCoTaskMemFree winCoTaskMemFree = stdlib
.lookupFunction<WinCoTaskMemFreeNative, WinCoTaskMemFree>('CoTaskMemFree');
typedef WinCoTaskMemFreeNative = Void Function(Pointer);
typedef WinCoTaskMemFree = void Function(Pointer);
final Pointer<NativeFunction<WinCoTaskMemFreeNative>> winCoTaskMemFreePointer =
stdlib.lookup('CoTaskMemFree');
final WinCoTaskMemFree winCoTaskMemFree = winCoTaskMemFreePointer.asFunction();

/// Manages memory on the native heap.
///
Expand All @@ -43,8 +45,8 @@ final WinCoTaskMemFree winCoTaskMemFree = stdlib
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
/// `CoTaskMemAlloc`.
class _MallocAllocator implements Allocator {
const _MallocAllocator();
final class MallocAllocator implements Allocator {
const MallocAllocator._();

/// Allocates [byteCount] bytes of of unitialized memory on the native heap.
///
Expand Down Expand Up @@ -81,6 +83,37 @@ class _MallocAllocator implements Allocator {
posixFree(pointer);
}
}

/// Returns a pointer to a native free function.
///
/// This function can be used to release memory allocated by [allocated]
/// from the native side. It can also be used as a finalization callback
/// passed to `NativeFinalizer` constructor or `Pointer.atTypedList`
/// method.
///
/// For example to automatically free native memory when the Dart object
/// wrapping it is reclaimed by GC:
///
/// ```dart
/// class Wrapper implements Finalizable {
/// static final finalizer = NativeFinalizer(malloc.nativeFree);
///
/// final Pointer<Uint8> data;
///
/// Wrapper() : data = malloc.allocate<Uint8>(length) {
/// finalizer.attach(this, data);
/// }
/// }
/// ```
///
/// or to free native memory that is owned by a typed list:
///
/// ```dart
/// malloc.allocate<Uint8>(n).asTypedList(n, finalizer: malloc.nativeFree)
/// ```
///
Pointer<NativeFinalizerFunction> get nativeFree =>
Platform.isWindows ? winCoTaskMemFreePointer : posixFreePointer;
}

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

/// 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
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
class _CallocAllocator implements Allocator {
const _CallocAllocator();
final class CallocAllocator implements Allocator {
const CallocAllocator._();

/// Fills a block of memory with a specified value.
void _fillMemory(Pointer destination, int length, int fill) {
Expand Down Expand Up @@ -153,6 +186,37 @@ class _CallocAllocator implements Allocator {
posixFree(pointer);
}
}

/// Returns a pointer to a native free function.
///
/// This function can be used to release memory allocated by [allocated]
/// from the native side. It can also be used as a finalization callback
/// passed to `NativeFinalizer` constructor or `Pointer.atTypedList`
/// method.
///
/// For example to automatically free native memory when the Dart object
/// wrapping it is reclaimed by GC:
///
/// ```dart
/// class Wrapper implements Finalizable {
/// static final finalizer = NativeFinalizer(calloc.nativeFree);
///
/// final Pointer<Uint8> data;
///
/// Wrapper() : data = calloc.allocate<Uint8>(length) {
/// finalizer.attach(this, data);
/// }
/// }
/// ```
///
/// or to free native memory that is owned by a typed list:
///
/// ```dart
/// calloc.allocate<Uint8>(n).asTypedList(n, finalizer: calloc.nativeFree)
/// ```
///
Pointer<NativeFinalizerFunction> get nativeFree =>
Platform.isWindows ? winCoTaskMemFreePointer : posixFreePointer;
}

/// Manages memory on the native heap.
Expand All @@ -162,4 +226,4 @@ class _CallocAllocator implements Allocator {
///
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
const Allocator calloc = _CallocAllocator();
const CallocAllocator calloc = CallocAllocator._();
2 changes: 1 addition & 1 deletion lib/src/utf16.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'package:ffi/ffi.dart';
/// through a `Pointer<Utf16>` representing the entire array. This pointer is
/// the equivalent of a char pointer (`const wchar_t*`) in C code. The
/// individual UTF-16 code units are stored in native byte order.
class Utf16 extends Opaque {}
final class Utf16 extends Opaque {}

/// Extension method for converting a`Pointer<Utf16>` to a [String].
extension Utf16Pointer on Pointer<Utf16> {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/utf8.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'package:ffi/ffi.dart';
/// The Utf8 type itself has no functionality, it's only intended to be used
/// through a `Pointer<Utf8>` representing the entire array. This pointer is
/// the equivalent of a char pointer (`const char*`) in C code.
class Utf8 extends Opaque {}
final class Utf8 extends Opaque {}

/// Extension method for converting a`Pointer<Utf8>` to a [String].
extension Utf8Pointer on Pointer<Utf8> {
Expand Down
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: ffi
version: 2.0.2
version: 2.1.0
description: Utilities for working with Foreign Function Interface (FFI) code.
repository: https://github.com/dart-lang/ffi

Expand All @@ -9,7 +9,7 @@ topics:
- codegen

environment:
sdk: '>=2.17.0 <4.0.0'
sdk: '>=3.0.0 <4.0.0'

dev_dependencies:
test: ^1.21.2
Expand Down
9 changes: 9 additions & 0 deletions test/allocation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ void main() async {
// amount of addressable memory on the system.
expect(() => calloc<Uint8>(-1), throwsA(isA<ArgumentError>()));
});

test('nativeFree', () {
// malloc.nativeFree should be able to free memory allocated by malloc.
final ptr1 = malloc.allocate<Uint8>(1024);
malloc.nativeFree.asFunction<void Function(Pointer<Void>)>()(ptr1.cast());
// calloc.nativeFree should be able to free memory allocated by calloc.
final ptr2 = calloc.allocate<Uint8>(1024);
calloc.nativeFree.asFunction<void Function(Pointer<Void>)>()(ptr2.cast());
});
}

0 comments on commit e2c01a9

Please sign in to comment.