Allocation utilities. (#7)
* Allocation utilities.
* Comments
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<PosixMallocNative, PosixMalloc>("malloc");
+
+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");
+
+/// 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}) {
+ final int totalSize = count * sizeOf<T>();
+ Pointer<T> 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: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 @@
/// Returns a malloc-allocated pointer to the result.
static Pointer<Utf16> toUtf16(String s) {
final units = s.codeUnits;
- final Pointer<Uint16> result =
- Pointer<Uint16>.allocate(count: units.length + 1);
+ final Pointer<Uint16> result = allocate<Uint16>(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: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<IntPtr>() == 8 ? _kMaxSmi64 : _kMaxSmi32;
@@ -54,7 +56,7 @@
static Pointer<Utf8> toUtf8(String string) {
final units = utf8.encode(string);
final Pointer<Uint8> result =
- Pointer<Uint8>.allocate(count: units.length + 1);
+ allocate<Uint8>(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 @@
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 @@
converted.cast<Uint16>().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:ffi/ffi.dart';
Pointer<Uint8> _bytesFromList(List<int> ints) {
- final Pointer<Uint8> ptr = Pointer.allocate(count: ints.length);
+ final Pointer<Uint8> ptr = allocate(count: ints.length);
final Uint8List list = ptr.asExternalTypedData(count: ints.length);
list.setAll(0, ints);
return ptr;
@@ -24,7 +24,7 @@
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 @@
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 @@
final Uint8List end =
converted.cast<Uint8>().asExternalTypedData(count: length + 1);
expect(end, equals([237, 160, 128, 225, 128, 128, 0]));
- converted.free();
+ free(converted);
});
test("fromUtf8 unpaired surrogate", () {