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

Expose pointer to free from allocators #203

Merged
merged 6 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
mraleph marked this conversation as resolved.
Show resolved Hide resolved
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.
mraleph marked this conversation as resolved.
Show resolved Hide resolved
///
/// 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());
});
}