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", () {