blob: 42021458ad1f7654a6fee31501aa341f751a63a9 [file] [log] [blame]
// 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 ole32.dll is the correct name in both 32-bit and 64-bit.
final DynamicLibrary stdlib = Platform.isWindows
? DynamicLibrary.open('ole32.dll')
: DynamicLibrary.process();
typedef PosixMallocNative = Pointer Function(IntPtr);
typedef PosixMalloc = Pointer Function(int);
final PosixMalloc posixMalloc =
stdlib.lookupFunction<PosixMallocNative, PosixMalloc>('malloc');
typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size);
typedef PosixCalloc = Pointer Function(int num, int size);
final PosixCalloc posixCalloc =
stdlib.lookupFunction<PosixCallocNative, PosixCalloc>('calloc');
typedef PosixFreeNative = Void Function(Pointer);
typedef PosixFree = void Function(Pointer);
final Pointer<NativeFunction<PosixFreeNative>> posixFreePointer =
stdlib.lookup('free');
final PosixFree posixFree = posixFreePointer.asFunction();
typedef WinCoTaskMemAllocNative = Pointer Function(Size);
typedef WinCoTaskMemAlloc = Pointer Function(int);
final WinCoTaskMemAlloc winCoTaskMemAlloc =
stdlib.lookupFunction<WinCoTaskMemAllocNative, WinCoTaskMemAlloc>(
'CoTaskMemAlloc');
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.
///
/// Does not initialize newly allocated memory to zero. Use [_CallocAllocator]
/// for zero-initialized memory on allocation.
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
/// `CoTaskMemAlloc`.
final class MallocAllocator implements Allocator {
const MallocAllocator._();
/// Allocates [byteCount] bytes of of unitialized memory on the native heap.
///
/// For POSIX-based systems, this uses `malloc`. On Windows, it uses
/// `CoTaskMemAlloc`.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
Pointer<T> result;
if (Platform.isWindows) {
result = winCoTaskMemAlloc(byteCount).cast();
} else {
result = posixMalloc(byteCount).cast();
}
if (result.address == 0) {
throw ArgumentError('Could not allocate $byteCount bytes.');
}
return result;
}
/// Releases memory allocated on the native heap.
///
/// For POSIX-based systems, this uses `free`. On Windows, it uses
/// `CoTaskMemFree`. It may only be used against pointers allocated in a
/// manner equivalent to [allocate].
@override
void free(Pointer pointer) {
if (Platform.isWindows) {
winCoTaskMemFree(pointer);
} else {
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.
///
/// Does not initialize newly allocated memory to zero. Use [calloc] for
/// zero-initialized memory allocation.
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
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`.
final class CallocAllocator implements Allocator {
const CallocAllocator._();
/// Fills a block of memory with a specified value.
void _fillMemory(Pointer destination, int length, int fill) {
final ptr = destination.cast<Uint8>();
for (var i = 0; i < length; i++) {
ptr[i] = fill;
}
}
/// Fills a block of memory with zeros.
///
void _zeroMemory(Pointer destination, int length) =>
_fillMemory(destination, length, 0);
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
/// heap.
///
/// For POSIX-based systems, this uses `malloc`. On Windows, it uses
/// `CoTaskMemAlloc`.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
// TODO: Stop ignoring alignment if it's large, for example for SSE data.
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
Pointer<T> result;
if (Platform.isWindows) {
result = winCoTaskMemAlloc(byteCount).cast();
} else {
result = posixCalloc(byteCount, 1).cast();
}
if (result.address == 0) {
throw ArgumentError('Could not allocate $byteCount bytes.');
}
if (Platform.isWindows) {
_zeroMemory(result, byteCount);
}
return result;
}
/// Releases memory allocated on the native heap.
///
/// For POSIX-based systems, this uses `free`. On Windows, it uses
/// `CoTaskMemFree`. It may only be used against pointers allocated in a
/// manner equivalent to [allocate].
@override
void free(Pointer pointer) {
if (Platform.isWindows) {
winCoTaskMemFree(pointer);
} else {
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.
///
/// Initializes newly allocated memory to zero. Use [malloc] for uninitialized
/// memory allocation.
///
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
/// `CoTaskMemAlloc` and `CoTaskMemFree`.
const CallocAllocator calloc = CallocAllocator._();