| // 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. |
| |
| part of "dart:ffi"; |
| |
| /// A memory range, represented by its starting address. |
| /// |
| /// Shared supertype of the FFI compound [Struct], [Union], and [Array] types. |
| /// |
| /// This class is not abstract because instances can be created as an anonymous |
| /// representation of a memory area, with no structure on top. (In particular, |
| /// during the transformation of `.address` in FFI leaf calls.) |
| @pragma("wasm:entry-point") |
| final class _Compound implements NativeType { |
| /// The underlying [TypedData] or [Pointer] that a subtype uses. |
| @pragma("vm:entry-point") |
| final Object _typedDataBase; |
| |
| /// Offset in bytes into [_typedDataBase]. |
| @pragma("vm:entry-point") |
| final int _offsetInBytes; |
| |
| external _Compound._(); |
| |
| @pragma('vm:prefer-inline') |
| _Compound._fromTypedDataBase(this._typedDataBase, this._offsetInBytes); |
| |
| /// Constructs a view on [typedData]. |
| /// |
| /// The length in bytes of [typedData] must at least be [sizeInBytes]. |
| @pragma('vm:prefer-inline') |
| _Compound._fromTypedData(TypedData typedData, int offset, int sizeInBytes) |
| : _typedDataBase = typedData, |
| _offsetInBytes = typedData.elementSizeInBytes * offset { |
| if (typedData.lengthInBytes < |
| typedData.elementSizeInBytes * offset + sizeInBytes) { |
| throw RangeError.range( |
| typedData.lengthInBytes, |
| sizeInBytes + typedData.elementSizeInBytes * offset, |
| null, |
| 'typedData.lengthInBytes', |
| 'The typed list is not large enough', |
| ); |
| } |
| } |
| } |
| |
| /// The supertype of all FFI struct types. |
| /// |
| /// FFI struct types should extend this class and declare fields corresponding |
| /// to the underlying native structure. |
| /// |
| /// Field declarations in a [Struct] subclass declaration are automatically |
| /// given a setter and getter implementation which accesses the native struct's |
| /// field in memory. |
| /// |
| /// All field declarations in a [Struct] subclass declaration must either have |
| /// type [int] or [double] and be annotated with a [NativeType] representing the |
| /// native type, or must be of type [Pointer], [Array] or a subtype of [Struct] |
| /// or [Union]. For example: |
| /// |
| /// ```c |
| /// typedef struct { |
| /// int a; |
| /// float b; |
| /// void* c; |
| /// } my_struct; |
| /// ``` |
| /// |
| /// ```dart |
| /// final class MyStruct extends Struct { |
| /// @Int32() |
| /// external int a; |
| /// |
| /// @Float() |
| /// external double b; |
| /// |
| /// external Pointer<Void> c; |
| /// } |
| /// ``` |
| /// |
| /// The field declarations of a [Struct] subclass *must* be marked `external`. A |
| /// struct subclass points directly into a location of native memory ([Pointer]) |
| /// or Dart memory ([TypedData]), and the external field's getter and setter |
| /// implementations directly read and write bytes at appropriate offsets from |
| /// that location. This does not allow for non-native fields to also exist. |
| /// |
| /// An instance of a struct subclass cannot be created with a generative |
| /// constructor. Instead, an instance can be created by [StructPointer.ref], |
| /// [Struct.create], FFI call return values, FFI callback arguments, |
| /// [StructArray], and accessing [Struct] fields. To create an instance backed |
| /// by native memory, use [StructPointer.ref]. To create an instance backed by |
| /// Dart memory, use [Struct.create]. |
| abstract base class Struct extends _Compound implements SizedNativeType { |
| /// Construct a reference to the [nullptr]. |
| /// |
| /// Use [StructPointer]'s `.ref` to gain references to native memory backed |
| /// structs. |
| Struct() : super._(); |
| |
| /// Creates a struct view of bytes in [typedData]. |
| /// |
| /// The created instance of the struct subclass will then be backed by the |
| /// bytes at [TypedData.offsetInBytes] plus [offset] times |
| /// [TypedData.elementSizeInBytes]. That is, the getters and setters of the |
| /// external instance variables declared by the subclass, will read an write |
| /// their values from the bytes of the [TypedData.buffer] of [typedData], |
| /// starting at [TypedData.offsetInBytes] plus [offset] times |
| /// [TypedData.elementSizeInBytes]. The [TypedData.lengthInBytes] of |
| /// [typedData] *must* be sufficient to contain the [sizeOf] of the struct |
| /// subclass. _It doesn't matter whether the [typedData] is, for example, a |
| /// [Uint8List], a [Float64List], or any other [TypedData], it's only treated |
| /// as a view into a [ByteBuffer], through its [TypedData.buffer], |
| /// [TypedData.offsetInBytes] and [TypedData.lengthInBytes]._ |
| /// |
| /// If [typedData] is omitted, a fresh [ByteBuffer], with precisely enough |
| /// bytes for the [sizeOf] of the created struct, is allocated on the Dart |
| /// heap, and used as memory to store the struct fields. |
| /// |
| /// If [offset] is provided, the indexing into [typedData] is offset by |
| /// [offset] times [TypedData.elementSizeInBytes]. |
| /// |
| /// Example: |
| /// |
| /// ```dart import:typed_data |
| /// final class Point extends Struct { |
| /// @Double() |
| /// external double x; |
| /// |
| /// @Double() |
| /// external double y; |
| /// |
| /// /// Creates Dart managed memory to hold a `Point` and returns the |
| /// /// `Point` view on it. |
| /// factory Point(double x, double y) { |
| /// return Struct.create() |
| /// ..x = x |
| /// ..y = y; |
| /// } |
| /// |
| /// /// Creates a [Point] view on [typedData]. |
| /// factory Point.fromTypedData(TypedData typedData) { |
| /// return Struct.create(typedData); |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// To create a struct object from a [Pointer], use [StructPointer.ref]. |
| @Since('3.4') |
| external static T create<T extends Struct>([TypedData typedData, int offset]); |
| |
| /// Creates a view on a [TypedData] or [Pointer]. |
| /// |
| /// Used in [StructPointer.ref], FFI calls, and FFI callbacks. |
| @pragma('vm:prefer-inline') |
| Struct._fromTypedDataBase(super._typedDataBase, super._offsetInBytes) |
| : super._fromTypedDataBase(); |
| |
| /// Creates a view on [typedData]. |
| /// |
| /// The length in bytes of [typedData] must at least be [sizeInBytes]. |
| /// |
| /// Used in the `external` public constructor of [Struct]. |
| @pragma('vm:prefer-inline') |
| Struct._fromTypedData(super.typedData, super.offset, super.sizeInBytes) |
| : super._fromTypedData(); |
| } |
| |
| /// Annotation to specify on `Struct` subtypes to indicate that its members |
| /// need to be packed. |
| /// |
| /// Valid values for [memberAlignment] are 1, 2, 4, 8, and 16. |
| @Since('2.13') |
| final class Packed { |
| final int memberAlignment; |
| |
| const Packed(this.memberAlignment); |
| } |