diff --git a/lib/ffi.dart b/lib/ffi.dart index 24ad6a6..4f7e546 100644 --- a/lib/ffi.dart +++ b/lib/ffi.dart @@ -4,3 +4,4 @@ export 'src/utf8.dart'; export 'src/utf16.dart'; +export 'src/allocation.dart' show allocate, free; diff --git a/lib/src/allocation.dart b/lib/src/allocation.dart new file mode 100644 index 0000000..c1b7277 --- /dev/null +++ b/lib/src/allocation.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2019, 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:ffi'; +import 'dart:io'; + +// Note that kernel32.dll is the correct name in both 32-bit and 64-bit. +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open("kernel32.dll") + : DynamicLibrary.process(); + +typedef PosixMallocNative = Pointer Function(IntPtr); +typedef PosixMalloc = Pointer Function(int); +final PosixMalloc posixMalloc = + stdlib.lookupFunction("malloc"); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction("free"); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction("GetProcessHeap"); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction("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("HeapFree"); + +/// 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 allocate({int count = 1}) { + final int totalSize = count * sizeOf(); + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ 0, totalSize).cast(); + } else { + result = posixMalloc(totalSize).cast(); + } + if (result.address == 0) { + throw ArgumentError("Could not allocate $totalSize bytes."); + } + return result; +} + +/// 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. +/// +// 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. +void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError("Could not free $pointer."); + } + } else { + posixFree(pointer); + } +} diff --git a/lib/src/utf16.dart b/lib/src/utf16.dart index e4b4f95..b7f7783 100644 --- a/lib/src/utf16.dart +++ b/lib/src/utf16.dart @@ -6,6 +6,8 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:typed_data'; +import 'package:ffi/ffi.dart'; + /// [Utf16] implements conversion between Dart strings and null-terminated /// Utf6-encoded "char*" strings in C. /// @@ -21,8 +23,7 @@ class Utf16 extends Struct { /// Returns a malloc-allocated pointer to the result. static Pointer toUtf16(String s) { final units = s.codeUnits; - final Pointer result = - Pointer.allocate(count: units.length + 1); + final Pointer result = allocate(count: units.length + 1); final Uint16List nativeString = result.asExternalTypedData(count: units.length + 1); nativeString.setAll(0, units); diff --git a/lib/src/utf8.dart b/lib/src/utf8.dart index 52d0e95..9fcc823 100644 --- a/lib/src/utf8.dart +++ b/lib/src/utf8.dart @@ -6,6 +6,8 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:typed_data'; +import 'package:ffi/ffi.dart'; + const int _kMaxSmi64 = (1 << 62) - 1; const int _kMaxSmi32 = (1 << 30) - 1; final int _maxSize = sizeOf() == 8 ? _kMaxSmi64 : _kMaxSmi32; @@ -54,7 +56,7 @@ class Utf8 extends Struct { static Pointer toUtf8(String string) { final units = utf8.encode(string); final Pointer result = - Pointer.allocate(count: units.length + 1); + allocate(count: units.length + 1); final Uint8List nativeString = result.asExternalTypedData(count: units.length + 1); nativeString.setAll(0, units); diff --git a/test/utf16_test.dart b/test/utf16_test.dart index 53cfd1c..99863e8 100644 --- a/test/utf16_test.dart +++ b/test/utf16_test.dart @@ -16,7 +16,7 @@ main() { converted.asExternalTypedData(count: start.codeUnits.length + 1); final matcher = equals(start.codeUnits.toList()..add(0)); expect(end, matcher); - converted.free(); + free(converted); }); test("toUtf16 emoji", () { @@ -27,6 +27,6 @@ main() { converted.cast().asExternalTypedData(count: length + 1); final matcher = equals(start.codeUnits.toList()..add(0)); expect(end, matcher); - converted.free(); + free(converted); }); } diff --git a/test/utf8_test.dart b/test/utf8_test.dart index cf4e97e..5b92fce 100644 --- a/test/utf8_test.dart +++ b/test/utf8_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; import 'package:ffi/ffi.dart'; Pointer _bytesFromList(List ints) { - final Pointer ptr = Pointer.allocate(count: ints.length); + final Pointer ptr = allocate(count: ints.length); final Uint8List list = ptr.asExternalTypedData(count: ints.length); list.setAll(0, ints); return ptr; @@ -24,7 +24,7 @@ main() { final matcher = equals([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10, 0]); expect(end, matcher); - converted.free(); + free(converted); }); test("fromUtf8 ASCII", () { @@ -43,7 +43,7 @@ main() { final matcher = equals([240, 159, 152, 142, 240, 159, 145, 191, 240, 159, 146, 172, 0]); expect(end, matcher); - converted.free(); + free(converted); }); test("formUtf8 emoji", () { @@ -60,7 +60,7 @@ main() { final Uint8List end = converted.cast().asExternalTypedData(count: length + 1); expect(end, equals([237, 160, 128, 225, 128, 128, 0])); - converted.free(); + free(converted); }); test("fromUtf8 unpaired surrogate", () {