// 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 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([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); }