blob: 858d53fb3aca756d2acb4ed687193f1bb2f77cdd [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 'package:ffi/ffi.dart';
main() {
print('start main');
{
// Basic operation: allocate, get, set, and free.
Pointer<Int64> p = calloc();
p.value = 42;
int pValue = p.value;
print('${p.runtimeType} value: ${pValue}');
calloc.free(p);
}
{
// Undefined behavior before set.
Pointer<Int64> p = calloc();
int pValue = p.value;
print('If not set, returns garbage: ${pValue}');
calloc.free(p);
}
{
// Pointers can be created from an address.
Pointer<Int64> pHelper = calloc();
pHelper.value = 1337;
int address = pHelper.address;
print('Address: ${address}');
Pointer<Int64> p = Pointer.fromAddress(address);
print('${p.runtimeType} value: ${p.value}');
calloc.free(pHelper);
}
{
// Address is zeroed out after free.
Pointer<Int64> p = calloc();
calloc.free(p);
print('After free, address is zero: ${p.address}');
}
{
// Allocating too much throws an exception.
try {
int maxMint = 9223372036854775807; // 2^63 - 1
calloc<Int64>(maxMint);
} on Error {
print('Expected exception on allocating too much');
}
try {
int maxInt1_8 = 1152921504606846975; // 2^60 -1
calloc<Int64>(maxInt1_8);
} on Error {
print('Expected exception on allocating too much');
}
}
{
// Pointers can be cast into another type,
// resulting in the corresponding bits read.
Pointer<Int64> p1 = calloc();
p1.value = 9223372036854775807; // 2^63 - 1
Pointer<Int32> p2 = p1.cast();
print('${p2.runtimeType} value: ${p2.value}'); // -1
Pointer<Int32> p3 = p2 + 1;
print('${p3.runtimeType} value: ${p3.value}'); // 2^31 - 1
calloc.free(p1);
}
{
// Data can be tightly packed in memory.
Pointer<Int8> p = calloc(8);
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
(p + i).value = i * 3;
}
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
print('p.elementAt($i) value: ${(p + i).value}');
}
calloc.free(p);
}
{
// Values that don't fit are truncated.
Pointer<Int32> p11 = calloc();
p11.value = 9223372036854775807;
print(p11);
calloc.free(p11);
}
{
// Doubles.
Pointer<Double> p = calloc();
p.value = 3.14159265359;
print('${p.runtimeType} value: ${p.value}');
p.value = 3.14;
print('${p.runtimeType} value: ${p.value}');
calloc.free(p);
}
{
// Floats.
Pointer<Float> p = calloc();
p.value = 3.14159265359;
print('${p.runtimeType} value: ${p.value}');
p.value = 3.14;
print('${p.runtimeType} value: ${p.value}');
calloc.free(p);
}
{
// IntPtr varies in size based on whether the platform is 32 or 64 bit.
// Addresses of pointers fit in this size.
Pointer<IntPtr> p = calloc();
int p14addr = p.address;
p.value = p14addr;
int pValue = p.value;
print('${p.runtimeType} value: ${pValue}');
calloc.free(p);
}
{
// Void pointers are unsized.
// The size of the element it is pointing to is undefined,
// they cannot be allocated, read, or written.
Pointer<IntPtr> p1 = calloc();
Pointer<Void> p2 = p1.cast();
print('${p2.runtimeType} address: ${p2.address}');
calloc.free(p1);
}
{
// Pointer to a pointer to something.
Pointer<Int16> pHelper = calloc();
pHelper.value = 17;
Pointer<Pointer<Int16>> p = calloc();
// Storing into a pointer pointer automatically unboxes.
p.value = pHelper;
// Reading from a pointer pointer automatically boxes.
Pointer<Int16> pHelper2 = p.value;
print('${pHelper2.runtimeType} value: ${pHelper2.value}');
int pValue = p.value.value;
print('${p.runtimeType} value\'s value: ${pValue}');
calloc.free(p);
calloc.free(pHelper);
}
{
// The pointer to pointer types must match up.
Pointer<Int8> pHelper = calloc();
pHelper.value = 123;
Pointer<Pointer<Int16>> p = calloc();
// Trying to store `pHelper` into `p.val` would result in a type mismatch.
calloc.free(pHelper);
calloc.free(p);
}
{
// `nullptr` points to address 0 in c++.
Pointer<Pointer<Int8>> pointerToPointer = calloc();
Pointer<Int8> value = nullptr;
pointerToPointer.value = value;
value = pointerToPointer.value;
print("Loading a pointer to the 0 address is null: ${value}");
calloc.free(pointerToPointer);
}
{
// The toplevel function sizeOf returns element size in bytes.
print('sizeOf<Double>(): ${sizeOf<Double>()}');
print('sizeOf<Int16>(): ${sizeOf<Int16>()}');
print('sizeOf<IntPtr>(): ${sizeOf<IntPtr>()}');
}
{
// With IntPtr pointers, one could manually setup arbitrary data
// structures in C memory.
//
// However, it is advised to use Pointer<Pointer<...>> for that.
void createChain(Pointer<IntPtr> head, int length, int value) {
if (length == 0) {
head.value = value;
return;
}
Pointer<IntPtr> next = calloc<IntPtr>();
head.value = next.address;
createChain(next, length - 1, value);
}
int getChainValue(Pointer<IntPtr> head, int length) {
if (length == 0) {
return head.value;
}
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
return getChainValue(next, length - 1);
}
void freeChain(Pointer<IntPtr> head, int length) {
Pointer<IntPtr> next = Pointer.fromAddress(head.value);
calloc.free(head);
if (length == 0) {
return;
}
freeChain(next, length - 1);
}
int length = 10;
Pointer<IntPtr> head = calloc();
createChain(head, length, 512);
int tailValue = getChainValue(head, length);
print('tailValue: ${tailValue}');
freeChain(head, length);
}
print("end main");
}