// 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' as ffi; main(List arguments) { print('start main'); { // basic operation: allocate, get, set, and free ffi.Pointer p = ffi.allocate(); p.store(42); int pValue = p.load(); print('${p.runtimeType} value: ${pValue}'); p.free(); } { // undefined behavior before set ffi.Pointer p = ffi.allocate(); int pValue = p.load(); print('If not set, returns garbage: ${pValue}'); p.free(); } { // pointers can be created from an address ffi.Pointer pHelper = ffi.allocate(); pHelper.store(1337); int address = pHelper.address; print('Address: ${address}'); ffi.Pointer p = ffi.fromAddress(address); print('${p.runtimeType} value: ${p.load()}'); pHelper.free(); } { // address is zeroed out after free ffi.Pointer p = ffi.allocate(); p.free(); print('After free, address is zero: ${p.address}'); } { // pointer arithmetic can be done with element offsets or bytes ffi.Pointer p1 = ffi.allocate(count: 2); print('p1 address: ${p1.address}'); ffi.Pointer p2 = p1.elementAt(1); print('p1.elementAt(1) address: ${p2.address}'); p2.store(100); ffi.Pointer p3 = p1.offsetBy(8); print('p1.offsetBy(8) address: ${p3.address}'); print('p1.offsetBy(8) value: ${p3.load()}'); p1.free(); } { // allocating too much throws an exception try { int maxMint = 9223372036854775807; // 2^63 - 1 ffi.allocate(count: maxMint); } on RangeError { print('Expected exception on allocating too much'); } try { int maxInt1_8 = 1152921504606846975; // 2^60 -1 ffi.allocate(count: maxInt1_8); } on ArgumentError { print('Expected exception on allocating too much'); } } { // pointers can be cast into another type // resulting in the corresponding bits read ffi.Pointer p1 = ffi.allocate(); p1.store(9223372036854775807); // 2^63 - 1 ffi.Pointer p2 = p1.cast(); print('${p2.runtimeType} value: ${p2.load()}'); // -1 ffi.Pointer p3 = p2.elementAt(1); print('${p3.runtimeType} value: ${p3.load()}'); // 2^31 - 1 p1.free(); } { // data can be tightly packed in memory ffi.Pointer p = ffi.allocate(count: 8); for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { p.elementAt(i).store(i * 3); } for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) { print('p.elementAt($i) value: ${p.elementAt(i).load()}'); } p.free(); } { // exception on storing a value that does not fit ffi.Pointer p11 = ffi.allocate(); try { p11.store(9223372036854775807); } on ArgumentError { print('Expected exception on calling set with a value that does not fit'); } p11.free(); } { // doubles ffi.Pointer p = ffi.allocate(); p.store(3.14159265359); print('${p.runtimeType} value: ${p.load()}'); p.store(3.14); print('${p.runtimeType} value: ${p.load()}'); p.free(); } { // floats ffi.Pointer p = ffi.allocate(); p.store(3.14159265359); print('${p.runtimeType} value: ${p.load()}'); p.store(3.14); print('${p.runtimeType} value: ${p.load()}'); p.free(); } { // ffi.IntPtr varies in size based on whether the platform is 32 or 64 bit // addresses of pointers fit in this size ffi.Pointer p = ffi.allocate(); int p14addr = p.address; p.store(p14addr); int pValue = p.load(); print('${p.runtimeType} value: ${pValue}'); p.free(); } { // void pointers are unsized // the size of the element it is pointing to is undefined // this means they cannot be ffi.allocated, read, or written // this would would fail to compile: // ffi.allocate(); ffi.Pointer p1 = ffi.allocate(); ffi.Pointer p2 = p1.cast(); print('${p2.runtimeType} address: ${p2.address}'); // this fails to compile, we cannot read something unsized // p2.load(); // this fails to compile, we cannot write something unsized // p2.store(1234); p1.free(); } { // pointer to a pointer to something ffi.Pointer pHelper = ffi.allocate(); pHelper.store(17); ffi.Pointer> p = ffi.allocate(); // storing into a pointer pointer automatically unboxes p.store(pHelper); // reading from a pointer pointer automatically boxes ffi.Pointer pHelper2 = p.load(); print('${pHelper2.runtimeType} value: ${pHelper2.load()}'); int pValue = p.load>().load(); print('${p.runtimeType} value\'s value: ${pValue}'); p.free(); pHelper.free(); } { // the pointer to pointer types must match up ffi.Pointer pHelper = ffi.allocate(); pHelper.store(123); ffi.Pointer> p = ffi.allocate(); // this fails to compile due to type mismatch // p.store(pHelper); pHelper.free(); p.free(); } { // null pointer in Dart points to address 0 in c++ ffi.Pointer> pointerToPointer = ffi.allocate(); ffi.Pointer value = null; pointerToPointer.store(value); value = pointerToPointer.load(); print("Loading a pointer to the 0 address is null: ${value}"); pointerToPointer.free(); } { // sizeof returns element size in bytes print('sizeOf(): ${ffi.sizeOf()}'); print('sizeOf(): ${ffi.sizeOf()}'); print('sizeOf(): ${ffi.sizeOf()}'); } { // only concrete sub types of NativeType can be ffi.allocated // this would fail to compile: // ffi.allocate(); } { // only concrete sub types of NativeType can be asked for size // this would fail to compile: // ffi.sizeOf(); } { // with ffi.IntPtr pointers, one can manually setup aribtrary data // structres in C memory. void createChain(ffi.Pointer head, int length, int value) { if (length == 0) { head.store(value); return; } ffi.Pointer next = ffi.allocate(); head.store(next.address); createChain(next, length - 1, value); } int getChainValue(ffi.Pointer head, int length) { if (length == 0) { return head.load(); } ffi.Pointer next = ffi.fromAddress(head.load()); return getChainValue(next, length - 1); } void freeChain(ffi.Pointer head, int length) { ffi.Pointer next = ffi.fromAddress(head.load()); head.free(); if (length == 0) { return; } freeChain(next, length - 1); } int length = 10; ffi.Pointer head = ffi.allocate(); createChain(head, length, 512); int tailValue = getChainValue(head, length); print('tailValue: ${tailValue}'); freeChain(head, length); } print("end main"); }