// 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

/// Foreign Function Interface for interoperability with the C programming language.
///
/// For further details, please see: https://dart.dev/server/c-interop.
///
/// {@category VM}
@Since('2.6')
library dart.ffi;

import 'dart:_internal' show Since;
import 'dart:isolate';
import 'dart:typed_data';

part 'abi.dart';
part 'abi_specific.dart';
part 'native_type.dart';
part 'native_finalizer.dart';
part 'allocation.dart';
part 'annotations.dart';
part 'c_type.dart';
part 'dynamic_library.dart';
part 'struct.dart';
part 'union.dart';

/// Number of bytes used by native type T.
///
/// Includes padding and alignment of structs.
///
/// This function must be invoked with a compile-time constant [T].
external int sizeOf<T extends SizedNativeType>();

/// Represents a pointer into the native C memory corresponding to 'NULL', e.g.
/// a pointer with address 0.
@pragma('vm:shared')
final Pointer<Never> nullptr = Pointer.fromAddress(0);

/// Represents a pointer into the native C memory. Cannot be extended.
@pragma('vm:entry-point')
@pragma("wasm:entry-point")
final class Pointer<T extends NativeType> implements SizedNativeType {
  /// Construction from raw integer.
  external factory Pointer.fromAddress(int ptr);

  /// Convert Dart function to a C function pointer, automatically marshalling
  /// the arguments and return value
  ///
  /// If an exception is thrown while calling `f()`, the native function will
  /// return `exceptionalReturn`, which must be assignable to return type of `f`.
  ///
  /// The returned function address can only be invoked on the mutator (main)
  /// thread of the current isolate. It will abort the process if invoked on any
  /// other thread. Use [NativeCallable.listener] to create callbacks that can
  /// be invoked from any thread.
  ///
  /// The pointer returned will remain alive for the duration of the current
  /// isolate's lifetime. After the isolate it was created in is terminated,
  /// invoking it from native code will cause undefined behavior.
  ///
  /// [Pointer.fromFunction] only accepts static or top level functions. Use
  /// [NativeCallable.isolateLocal] to create callbacks from any Dart function
  /// or closure.
  external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
    @DartRepresentationOf('T') Function f, [
    Object? exceptionalReturn,
  ]);

  /// Access to the raw pointer value.
  /// On 32-bit systems, the upper 32-bits of the result are 0.
  external int get address;

  /// Reinterprets the address of this pointer as the address of a [U].
  ///
  /// This function is completely unchecked, no attempts are made to validate
  /// that the bytes at the address are actually a valid native value
  /// of type [U].
  external Pointer<U> cast<U extends NativeType>();

  /// Equality for Pointers only depends on their address.
  bool operator ==(Object other) {
    if (other is! Pointer) return false;
    Pointer otherPointer = other;
    return address == otherPointer.address;
  }

  /// The hash code for a Pointer only depends on its address.
  int get hashCode {
    return address.hashCode;
  }
}

/// A fixed-sized array of [T]s.
@Since('2.13')
final class Array<T extends NativeType> extends _Compound {
  /// Annotation to specify [Array] dimensions in [Struct]s.
  ///
  /// ```dart
  /// final class MyStruct extends Struct {
  ///   @Array(8)
  ///   external Array<Uint8> inlineArray;
  ///
  ///   @Array(2, 2, 2)
  ///   external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
  /// }
  /// ```
  ///
  /// Do not invoke in normal code.
  const factory Array(
    int dimension1, [
    int dimension2,
    int dimension3,
    int dimension4,
    int dimension5,
  ]) = _ArraySize<T>;

  /// Annotation to specify [Array] dimensions in [Struct]s.
  ///
  /// ```dart
  /// final class MyStruct extends Struct {
  ///   @Array.multi([2, 2, 2])
  ///   external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
  ///
  ///   @Array.multi([2, 2, 2, 2, 2, 2, 2, 2])
  ///   external Array<Array<Array<Array<Array<Array<Array<Array<Uint8>>>>>>>> eightDimensionalInlineArray;
  /// }
  /// ```
  ///
  /// Do not invoke in normal code.
  const factory Array.multi(List<int> dimensions) = _ArraySize<T>.multi;

  /// Annotation to specify a variable length [Array] in [Struct]s.
  ///
  /// Can only be used on the last field of a struct. The last field of the
  /// struct is _not_ taken into account in [sizeOf]. Using an
  /// [AllocatorAlloc.call] will _not_ allocate any backing storage for the
  /// variable length array. Instead use [Allocator.allocate] and calculate the
  /// required number of bytes manually.
  ///
  /// ```dart
  /// import 'dart:ffi';
  /// import 'package:ffi/ffi.dart';
  ///
  /// final class MyStruct extends Struct {
  ///   @Size()
  ///   external int length;
  ///
  ///   @Array.variable()
  ///   external Array<Uint8> inlineArray;
  ///
  ///   static Pointer<MyStruct> allocate(Allocator allocator, int length) {
  ///     final lengthInBytes = sizeOf<MyStruct>() + sizeOf<Uint8>() * length;
  ///     final result = allocator.allocate<MyStruct>(lengthInBytes);
  ///     result.ref.length = length;
  ///     return result;
  ///   }
  /// }
  ///
  /// void main() {
  ///   final myStruct = MyStruct.allocate(calloc, 10);
  /// }
  /// ```
  ///
  /// The variable length is always the outermost dimension of the array.
  ///
  /// ```dart
  /// import 'dart:ffi';
  /// import 'package:ffi/ffi.dart';
  ///
  /// final class MyStruct extends Struct {
  ///   @Size()
  ///   external int length;
  ///
  ///   @Array.variable(10, 10)
  ///   external Array<Array<Array<Uint8>>> inlineArray;
  ///
  ///   static Pointer<MyStruct> allocate(Allocator allocator, int length) {
  ///     final lengthInBytes = sizeOf<MyStruct>() + sizeOf<Uint8>() * length * 100;
  ///     final result = allocator.allocate<MyStruct>(lengthInBytes);
  ///     result.ref.length = length;
  ///     return result;
  ///   }
  /// }
  /// ```
  ///
  /// Accessing variable length inline arrays of structs passed by value in FFI
  /// calls and callbacks is undefined behavior. Accessing variable length
  /// inline arrays in structs passed by value is undefined behavior in C.
  ///
  /// For more information about variable length inline arrays in C, please
  /// refer to: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html.
  ///
  /// Do not invoke in normal code.
  @Since('3.6')
  const factory Array.variable([
    int dimension2,
    int dimension3,
    int dimension4,
    int dimension5,
  ]) = _ArraySize<T>.variable;

  /// Annotation to specify a variable length [Array] with a configurable
  /// variable dimension ([dimension1]) in [Struct]s.
  ///
  /// Can only be used on the last field of a struct. When [dimension1] is set
  /// to a value greater than zero (`0`), the last field of the struct is taken
  /// into account in [sizeOf] and [AllocatorAlloc.call]. This is particularly
  /// useful when working with Windows APIs, where most structs with variable
  /// length arrays are defined to have an initial dimension of one (`1`).
  ///
  /// ```dart
  /// import 'dart:ffi';
  /// import 'package:ffi/ffi.dart';
  ///
  /// final class MyStruct extends Struct {
  ///   @Size()
  ///   external int length;
  ///
  ///   @Array.variableWithVariableDimension(1)
  ///   external Array<Uint8> inlineArray;
  ///
  ///   static Pointer<MyStruct> allocate(Allocator allocator, int length) {
  ///     final lengthInBytes = sizeOf<MyStruct>() + sizeOf<Uint8>() * (length - 1);
  ///     final result = allocator.allocate<MyStruct>(lengthInBytes);
  ///     result.ref.length = length;
  ///     return result;
  ///   }
  /// }
  ///
  /// void main() {
  ///   final myStruct = MyStruct.allocate(calloc, 10);
  /// }
  /// ```
  ///
  /// The variable length is always the outermost dimension of the array.
  ///
  /// Do not invoke in normal code.
  @Since('3.7')
  const factory Array.variableWithVariableDimension([
    int dimension1,
    int dimension2,
    int dimension3,
    int dimension4,
    int dimension5,
  ]) = _ArraySize<T>.variableWithVariableDimension;

  /// Annotation to a variable length [Array] in [Struct]s.
  ///
  /// ```dart
  /// final class MyStruct extends Struct {
  ///   @Array.variableMulti([2, 2])
  ///   external Array<Array<Array<Uint8>>> threeDimensionalInlineArray;
  /// }
  ///
  /// final class MyStruct2 extends Struct {
  ///   @Array.variableMulti(variableDimension: 1, [2, 2, 2])
  ///   external Array<Array<Array<Array<Uint8>>>> fourDimensionalInlineArray;
  /// }
  ///
  /// final class MyStruct3 extends Struct {
  ///   @Array.variableMulti([2, 2, 2, 2, 2, 2, 2])
  ///   external Array<Array<Array<Array<Array<Array<Array<Array<Uint8>>>>>>>> eightDimensionalInlineArray;
  /// }
  /// ```
  ///
  /// The variable length is always the outermost dimension of the array.
  ///
  /// [variableDimension] is the outermost dimension of the variable length
  /// array (defaults to zero (`0`)). When [variableDimension] is set to a value
  /// greater than zero (`0`), the last field of the struct is taken into
  /// account in [sizeOf] and [AllocatorAlloc.call].
  ///
  /// Do not invoke in normal code.
  @Since('3.6')
  const factory Array.variableMulti(
    List<int> dimensions, {
    @Since('3.7') int variableDimension,
  }) = _ArraySize<T>.variableMulti;
}

final class _ArraySize<T extends NativeType> implements Array<T> {
  final int? dimension1;
  final int? dimension2;
  final int? dimension3;
  final int? dimension4;
  final int? dimension5;

  final List<int>? dimensions;

  // When non-null, [dimension1] is equal to this value, or [dimensions] should
  // be prepended with this value.
  final int? variableDimension;

  const _ArraySize(
    this.dimension1, [
    this.dimension2,
    this.dimension3,
    this.dimension4,
    this.dimension5,
  ]) : dimensions = null,
       variableDimension = null;

  const _ArraySize.multi(this.dimensions)
    : dimension1 = null,
      dimension2 = null,
      dimension3 = null,
      dimension4 = null,
      dimension5 = null,
      variableDimension = null;

  const _ArraySize.variable([
    this.dimension2,
    this.dimension3,
    this.dimension4,
    this.dimension5,
  ]) : dimension1 = 0,
       dimensions = null,
       variableDimension = 0;

  const _ArraySize.variableWithVariableDimension([
    this.dimension1,
    this.dimension2,
    this.dimension3,
    this.dimension4,
    this.dimension5,
  ]) : dimensions = null,
       variableDimension = dimension1;

  const _ArraySize.variableMulti(
    List<int> nestedDimensions, {
    int variableDimension = 0,
  }) : // Should be `[variableDimension, ...nestedDimensions]`.
       dimensions = nestedDimensions,
       dimension1 = null,
       dimension2 = null,
       dimension3 = null,
       dimension4 = null,
       dimension5 = null,
       variableDimension = variableDimension;
}

/// Extension on [Pointer] specialized for the type argument [NativeFunction].
extension NativeFunctionPointer<NF extends Function>
    on Pointer<NativeFunction<NF>> {
  /// Convert to Dart function, automatically marshalling the arguments and
  /// return value.
  ///
  /// [isLeaf] specifies whether the function is a leaf function. Leaf functions
  /// are small, short-running, non-blocking functions which are not allowed to
  /// call back into Dart or use any Dart VM APIs. Leaf functions are invoked
  /// bypassing some of the heavier parts of the standard Dart-to-Native calling
  /// sequence which reduces the invocation overhead, making leaf calls faster
  /// than non-leaf calls. However, this implies that a thread executing a leaf
  /// function can't cooperate with the Dart runtime. A long running or blocking
  /// leaf function will delay any operation which requires synchronization
  /// between all threads associated with an isolate group until after the leaf
  /// function returns. For example, if one isolate in a group is trying to
  /// perform a GC and a second isolate is blocked in a leaf call, then the
  /// first isolate will have to pause and wait until this leaf call returns.
  external DF asFunction<@DartRepresentationOf('NF') DF extends Function>({
    bool isLeaf = false,
  });
}

/// A native callable which listens for calls to a native function.
///
/// Creates a native function linked to a Dart function, so that calling the
/// native function will call the Dart function in some way, with the arguments
/// converted to Dart values.
@Since('3.1')
abstract final class NativeCallable<T extends Function> {
  /// Constructs a [NativeCallable] that must be invoked from the same thread
  /// that created it.
  ///
  /// If an exception is thrown by the [callback], the native function will
  /// return the `exceptionalReturn`, which must be assignable to the return
  /// type of the [callback].
  ///
  /// The returned function address can only be invoked on the mutator (main)
  /// thread of the current isolate. It will abort the process if invoked on any
  /// other thread. Use [NativeCallable.listener] to create callbacks that can
  /// be invoked from any thread.
  ///
  /// Unlike [Pointer.fromFunction], [NativeCallable]s can be constructed from
  /// any Dart function or closure, not just static or top level functions.
  ///
  /// This callback must be [close]d when it is no longer needed. The [Isolate]
  /// that created the callback will be kept alive until [close] is called.
  /// After [NativeCallable.close] is called, invoking the [nativeFunction] from
  /// native code will cause undefined behavior.
  factory NativeCallable.isolateLocal(
    @DartRepresentationOf("T") Function callback, {
    Object? exceptionalReturn,
  }) {
    throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
  }

  /// Constructs a [NativeCallable] that can be invoked from any thread.
  ///
  /// When the native code invokes the function [nativeFunction],
  /// the [callback] will be executed within the isolate group
  /// of the [Isolate] which originally constructed the callable.
  /// Specifically, this means that an attempt to access any
  /// static or global field which is not shared between
  /// isolates in a group will result in a [Error].
  ///
  /// If an exception is thrown by the [callback], the
  /// native function will return the `exceptionalReturn`,
  /// which must be assignable to the return type of
  /// the [callback].
  ///
  /// [callback] and [exceptionalReturn] must be
  /// _trivially shareable_.
  ///
  /// This callback must be [close]d when it is no longer
  /// needed. An [Isolate] that created the callback will
  /// be kept alive until [close] is called.
  ///
  /// After [NativeCallable.close] is called, invoking
  /// the [nativeFunction] from native code will cause
  /// undefined behavior.
  ///
  /// NOTE: This is an experimental feature and may change in the future.
  factory NativeCallable.isolateGroupBound(
    @DartRepresentationOf("T") Function callback, {
    Object? exceptionalReturn,
  }) {
    throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
  }

  /// Constructs a [NativeCallable] that can be invoked from any thread.
  ///
  /// When the native code invokes the function [nativeFunction], the arguments
  /// will be sent over a [SendPort] to the [Isolate] that created the
  /// [NativeCallable], and the callback will be invoked.
  ///
  /// The native code does not wait for a response from the callback, so only
  /// functions returning void are supported.
  ///
  /// The callback will be invoked at some time in the future. The native caller
  /// cannot assume the callback will be run immediately. Resources passed to
  /// the callback (such as pointers to malloc'd memory, or output parameters)
  /// must be valid until the call completes.
  ///
  /// This callback must be [close]d when it is no longer needed. The [Isolate]
  /// that created the callback will be kept alive until [close] is called.
  /// After [NativeCallable.close] is called, invoking the [nativeFunction] from
  /// native code will cause undefined behavior.
  ///
  /// For example:
  ///
  /// ```dart
  /// import 'dart:async';
  /// import 'dart:ffi';
  /// import 'package:ffi/ffi.dart';
  ///
  /// // Processes a simple HTTP GET request using a native HTTP library that
  /// // processes the request on a background thread.
  /// Future<String> httpGet(String uri) async {
  ///   final uriPointer = uri.toNativeUtf8();
  ///
  ///   // Create the NativeCallable.listener.
  ///   final completer = Completer<String>();
  ///   late final NativeCallable<NativeHttpCallback> callback;
  ///   void onResponse(Pointer<Utf8> responsePointer) {
  ///     completer.complete(responsePointer.toDartString());
  ///     calloc.free(responsePointer);
  ///     calloc.free(uriPointer);
  ///
  ///     // Remember to close the NativeCallable once the native API is
  ///     // finished with it, otherwise this isolate will stay alive
  ///     // indefinitely.
  ///     callback.close();
  ///   }
  ///   callback = NativeCallable<NativeHttpCallback>.listener(onResponse);
  ///
  ///   // Invoke the native HTTP API. Our example HTTP library processes our
  ///   // request on a background thread, and calls the callback on that same
  ///   // thread when it receives the response.
  ///   nativeHttpGet(uriPointer, callback.nativeFunction);
  ///
  ///   return completer.future;
  /// }
  ///
  /// // Load the native functions from a DynamicLibrary.
  /// final DynamicLibrary dylib = DynamicLibrary.process();
  /// typedef NativeHttpCallback = Void Function(Pointer<Utf8>);
  ///
  /// typedef HttpGetFunction = void Function(
  ///     Pointer<Utf8>, Pointer<NativeFunction<NativeHttpCallback>>);
  /// typedef HttpGetNativeFunction = Void Function(
  ///     Pointer<Utf8>, Pointer<NativeFunction<NativeHttpCallback>>);
  /// final nativeHttpGet =
  ///     dylib.lookupFunction<HttpGetNativeFunction, HttpGetFunction>(
  ///         'http_get');
  /// ```
  factory NativeCallable.listener(
    @DartRepresentationOf("T") Function callback,
  ) {
    throw UnsupportedError("NativeCallable cannot be constructed dynamically.");
  }

  /// The native function pointer which can be used to invoke the `callback`
  /// passed to the constructor.
  ///
  /// This pointer must not be read after the callable has been [close]d.
  Pointer<NativeFunction<T>> get nativeFunction;

  /// Closes this callback and releases its resources.
  ///
  /// Further calls to existing [nativeFunction]s will result in undefined
  /// behavior.
  ///
  /// Subsequent calls to [close] will be ignored.
  ///
  /// It is safe to call [close] inside the [callback].
  void close();

  /// Whether this [NativeCallable] keeps its [Isolate] alive.
  ///
  /// By default, [NativeCallable]s keep the [Isolate] that created them alive
  /// until [close] is called. If [keepIsolateAlive] is set to `false`, the
  /// isolate may exit even if the [NativeCallable] isn't closed.
  external bool get keepIsolateAlive;
  external set keepIsolateAlive(bool value);
}

//
// Generated code, do not edit!
//
// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`.
//

/// Extension on [Pointer] specialized for the type argument [Int8].
extension Int8Pointer on Pointer<Int8> {
  /// The 8-bit two's complement integer at [address].
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toSigned(8)`) before
  /// being stored, and the 8-bit value is sign-extended when it is loaded.
  external int get value;

  external void set value(int value);

  /// The 8-bit two's complement integer at `address + sizeOf<Int8>() * index`.
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toSigned(8)`) before
  /// being stored, and the 8-bit value is sign-extended when it is loaded.
  external int operator [](int index);

  /// The 8-bit two's complement integer at `address + sizeOf<Int8>() * index`.
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toSigned(8)`) before
  /// being stored, and the 8-bit value is sign-extended when it is loaded.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Int8> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Int8>() * index);

  /// A pointer to the [offset]th [Int8] after this one.
  ///
  /// Returns a pointer to the [Int8] whose address is
  /// [offset] times the size of `Int8` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Int8>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int8> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Int8>() * offset);

  /// A pointer to the [offset]th [Int8] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Int8] whose address is
  /// [offset] times the size of `Int8` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Int8>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int8> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Int8>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Int8>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  external Int8List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Int16].
extension Int16Pointer on Pointer<Int16> {
  /// The 16-bit two's complement integer at [address].
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toSigned(16)`) before
  /// being stored, and the 16-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 16-bit two's complement integer at `address + sizeOf<Int16>() * index`.
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toSigned(16)`) before
  /// being stored, and the 16-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external int operator [](int index);

  /// The 16-bit two's complement integer at `address + sizeOf<Int16>() * index`.
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toSigned(16)`) before
  /// being stored, and the 16-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Int16> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Int16>() * index);

  /// A pointer to the [offset]th [Int16] after this one.
  ///
  /// Returns a pointer to the [Int16] whose address is
  /// [offset] times the size of `Int16` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Int16>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int16> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Int16>() * offset);

  /// A pointer to the [offset]th [Int16] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Int16] whose address is
  /// [offset] times the size of `Int16` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Int16>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int16> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Int16>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Int16>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 2-byte aligned.
  external Int16List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Int32].
extension Int32Pointer on Pointer<Int32> {
  /// The 32-bit two's complement integer at [address].
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toSigned(32)`) before
  /// being stored, and the 32-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 32-bit two's complement integer at `address + sizeOf<Int32>() * index`.
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toSigned(32)`) before
  /// being stored, and the 32-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external int operator [](int index);

  /// The 32-bit two's complement integer at `address + sizeOf<Int32>() * index`.
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toSigned(32)`) before
  /// being stored, and the 32-bit value is sign-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Int32> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Int32>() * index);

  /// A pointer to the [offset]th [Int32] after this one.
  ///
  /// Returns a pointer to the [Int32] whose address is
  /// [offset] times the size of `Int32` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Int32>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int32> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Int32>() * offset);

  /// A pointer to the [offset]th [Int32] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Int32] whose address is
  /// [offset] times the size of `Int32` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Int32>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int32> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Int32>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Int32>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 4-byte aligned.
  external Int32List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Int64].
extension Int64Pointer on Pointer<Int64> {
  /// The 64-bit two's complement integer at [address].
  ///
  /// The [address] must be 8-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 64-bit two's complement integer at `address + sizeOf<Int64>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external int operator [](int index);

  /// The 64-bit two's complement integer at `address + sizeOf<Int64>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Int64> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Int64>() * index);

  /// A pointer to the [offset]th [Int64] after this one.
  ///
  /// Returns a pointer to the [Int64] whose address is
  /// [offset] times the size of `Int64` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Int64>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int64> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Int64>() * offset);

  /// A pointer to the [offset]th [Int64] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Int64] whose address is
  /// [offset] times the size of `Int64` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Int64>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Int64> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Int64>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Int64>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 8-byte aligned.
  external Int64List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Uint8].
extension Uint8Pointer on Pointer<Uint8> {
  /// The 8-bit unsigned integer at [address].
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toUnsigned(8)`) before
  /// being stored, and the 8-bit value is zero-extended when it is loaded.
  external int get value;

  external void set value(int value);

  /// The 8-bit unsigned integer at `address + sizeOf<Uint8>() * index`.
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toUnsigned(8)`) before
  /// being stored, and the 8-bit value is zero-extended when it is loaded.
  external int operator [](int index);

  /// The 8-bit unsigned integer at `address + sizeOf<Uint8>() * index`.
  ///
  /// A Dart integer is truncated to 8 bits (as if by `.toUnsigned(8)`) before
  /// being stored, and the 8-bit value is zero-extended when it is loaded.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Uint8> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Uint8>() * index);

  /// A pointer to the [offset]th [Uint8] after this one.
  ///
  /// Returns a pointer to the [Uint8] whose address is
  /// [offset] times the size of `Uint8` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Uint8>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint8> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Uint8>() * offset);

  /// A pointer to the [offset]th [Uint8] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Uint8] whose address is
  /// [offset] times the size of `Uint8` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Uint8>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint8> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Uint8>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Uint8>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  external Uint8List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Uint16].
extension Uint16Pointer on Pointer<Uint16> {
  /// The 16-bit unsigned integer at [address].
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toUnsigned(16)`) before
  /// being stored, and the 16-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 16-bit unsigned integer at `address + sizeOf<Uint16>() * index`.
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toUnsigned(16)`) before
  /// being stored, and the 16-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external int operator [](int index);

  /// The 16-bit unsigned integer at `address + sizeOf<Uint16>() * index`.
  ///
  /// A Dart integer is truncated to 16 bits (as if by `.toUnsigned(16)`) before
  /// being stored, and the 16-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 2-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Uint16> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Uint16>() * index);

  /// A pointer to the [offset]th [Uint16] after this one.
  ///
  /// Returns a pointer to the [Uint16] whose address is
  /// [offset] times the size of `Uint16` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Uint16>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint16> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Uint16>() * offset);

  /// A pointer to the [offset]th [Uint16] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Uint16] whose address is
  /// [offset] times the size of `Uint16` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Uint16>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint16> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Uint16>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Uint16>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 2-byte aligned.
  external Uint16List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Uint32].
extension Uint32Pointer on Pointer<Uint32> {
  /// The 32-bit unsigned integer at [address].
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toUnsigned(32)`) before
  /// being stored, and the 32-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 32-bit unsigned integer at `address + sizeOf<Uint32>() * index`.
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toUnsigned(32)`) before
  /// being stored, and the 32-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external int operator [](int index);

  /// The 32-bit unsigned integer at `address + sizeOf<Uint32>() * index`.
  ///
  /// A Dart integer is truncated to 32 bits (as if by `.toUnsigned(32)`) before
  /// being stored, and the 32-bit value is zero-extended when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Uint32> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Uint32>() * index);

  /// A pointer to the [offset]th [Uint32] after this one.
  ///
  /// Returns a pointer to the [Uint32] whose address is
  /// [offset] times the size of `Uint32` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Uint32>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint32> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Uint32>() * offset);

  /// A pointer to the [offset]th [Uint32] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Uint32] whose address is
  /// [offset] times the size of `Uint32` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Uint32>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint32> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Uint32>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Uint32>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 4-byte aligned.
  external Uint32List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Uint64].
extension Uint64Pointer on Pointer<Uint64> {
  /// The 64-bit unsigned integer at [address].
  ///
  /// The [address] must be 8-byte aligned.
  external int get value;

  external void set value(int value);

  /// The 64-bit unsigned integer at `address + sizeOf<Uint64>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external int operator [](int index);

  /// The 64-bit unsigned integer at `address + sizeOf<Uint64>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Uint64> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Uint64>() * index);

  /// A pointer to the [offset]th [Uint64] after this one.
  ///
  /// Returns a pointer to the [Uint64] whose address is
  /// [offset] times the size of `Uint64` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Uint64>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint64> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Uint64>() * offset);

  /// A pointer to the [offset]th [Uint64] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Uint64] whose address is
  /// [offset] times the size of `Uint64` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Uint64>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Uint64> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Uint64>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Uint64>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 8-byte aligned.
  external Uint64List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Float].
extension FloatPointer on Pointer<Float> {
  /// The float at [address].
  ///
  /// A Dart double loses precision before being stored, and the float value is
  /// converted to a double when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external double get value;

  external void set value(double value);

  /// The float at `address + sizeOf<Float>() * index`.
  ///
  /// A Dart double loses precision before being stored, and the float value is
  /// converted to a double when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external double operator [](int index);

  /// The float at `address + sizeOf<Float>() * index`.
  ///
  /// A Dart double loses precision before being stored, and the float value is
  /// converted to a double when it is loaded.
  ///
  /// The [address] must be 4-byte aligned.
  external void operator []=(int index, double value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Float> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Float>() * index);

  /// A pointer to the [offset]th [Float] after this one.
  ///
  /// Returns a pointer to the [Float] whose address is
  /// [offset] times the size of `Float` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Float>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Float> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Float>() * offset);

  /// A pointer to the [offset]th [Float] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Float] whose address is
  /// [offset] times the size of `Float` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Float>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Float> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Float>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Float>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 4-byte aligned.
  external Float32List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Double].
extension DoublePointer on Pointer<Double> {
  /// The double at [address].
  ///
  /// The [address] must be 8-byte aligned.
  external double get value;

  external void set value(double value);

  /// The double at `address + sizeOf<Double>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external double operator [](int index);

  /// The double at `address + sizeOf<Double>() * index`.
  ///
  /// The [address] must be 8-byte aligned.
  external void operator []=(int index, double value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Double> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Double>() * index);

  /// A pointer to the [offset]th [Double] after this one.
  ///
  /// Returns a pointer to the [Double] whose address is
  /// [offset] times the size of `Double` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Double>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Double> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Double>() * offset);

  /// A pointer to the [offset]th [Double] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Double] whose address is
  /// [offset] times the size of `Double` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Double>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Double> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Double>() * offset);

  /// Creates a typed list view backed by memory in the address space.
  ///
  /// The returned view will allow access to the memory range from [address]
  /// to `address + sizeOf<Double>() * length`.
  ///
  /// The user has to ensure the memory range is accessible while using the
  /// returned list.
  ///
  /// If provided, [finalizer] will be run on the pointer once the typed list
  /// is GCed. If provided, [token] will be passed to [finalizer], otherwise
  /// the this pointer itself will be passed.
  ///
  /// The [address] must be 8-byte aligned.
  external Float64List asTypedList(
    int length, {
    @Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
    @Since('3.1') Pointer<Void>? token,
  });
}

/// Extension on [Pointer] specialized for the type argument [Bool].
@Since('2.15')
extension BoolPointer on Pointer<Bool> {
  /// The bool at [address].
  external bool get value;

  external void set value(bool value);

  /// The bool at `address + sizeOf<Bool>() * index`.
  external bool operator [](int index);

  /// The bool at `address + sizeOf<Bool>() * index`.
  external void operator []=(int index, bool value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  Pointer<Bool> elementAt(int index) =>
      Pointer.fromAddress(address + sizeOf<Bool>() * index);

  /// A pointer to the [offset]th [Bool] after this one.
  ///
  /// Returns a pointer to the [Bool] whose address is
  /// [offset] times the size of `Bool` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Bool>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Bool> operator +(int offset) =>
      Pointer.fromAddress(address + sizeOf<Bool>() * offset);

  /// A pointer to the [offset]th [Bool] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Bool] whose address is
  /// [offset] times the size of `Bool` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Bool>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  @Since('3.3')
  @pragma("vm:prefer-inline")
  Pointer<Bool> operator -(int offset) =>
      Pointer.fromAddress(address - sizeOf<Bool>() * offset);
}

/// Bounds checking indexing methods on [Array]s of [Int8].
@Since('2.13')
extension Int8Array on Array<Int8> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Int8List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Int16].
@Since('2.13')
extension Int16Array on Array<Int16> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Int16List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Int32].
@Since('2.13')
extension Int32Array on Array<Int32> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Int32List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Int64].
@Since('2.13')
extension Int64Array on Array<Int64> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Int64List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Uint8].
@Since('2.13')
extension Uint8Array on Array<Uint8> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Uint8List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Uint16].
@Since('2.13')
extension Uint16Array on Array<Uint16> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Uint16List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Uint32].
@Since('2.13')
extension Uint32Array on Array<Uint32> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Uint32List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Uint64].
@Since('2.13')
extension Uint64Array on Array<Uint64> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Uint64List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Float].
@Since('2.13')
extension FloatArray on Array<Float> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external double operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, double value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Float32List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Double].
@Since('2.13')
extension DoubleArray on Array<Double> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external double operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, double value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external Float64List get elements;
}

/// Bounds checking indexing methods on [Array]s of [Bool].
@Since('2.15')
extension BoolArray on Array<Bool> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external bool operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, bool value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external List<bool> get elements;
}

@Since('3.5')
extension Int8ListAddress on Int8List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int8> pointer);
  ///
  /// void main() {
  ///   final list = Int8List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Int8> get address;
}

@Since('3.5')
extension Int16ListAddress on Int16List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Int16>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int16> pointer);
  ///
  /// void main() {
  ///   final list = Int16List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Int16> get address;
}

@Since('3.5')
extension Int32ListAddress on Int32List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Int32>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int32> pointer);
  ///
  /// void main() {
  ///   final list = Int32List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Int32> get address;
}

@Since('3.5')
extension Int64ListAddress on Int64List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Int64>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int64> pointer);
  ///
  /// void main() {
  ///   final list = Int64List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Int64> get address;
}

@Since('3.5')
extension Uint8ListAddress on Uint8List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Uint8>)>(isLeaf: true)
  /// external void myFunction(Pointer<Uint8> pointer);
  ///
  /// void main() {
  ///   final list = Uint8List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Uint8> get address;
}

@Since('3.5')
extension Uint16ListAddress on Uint16List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Uint16>)>(isLeaf: true)
  /// external void myFunction(Pointer<Uint16> pointer);
  ///
  /// void main() {
  ///   final list = Uint16List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Uint16> get address;
}

@Since('3.5')
extension Uint32ListAddress on Uint32List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Uint32>)>(isLeaf: true)
  /// external void myFunction(Pointer<Uint32> pointer);
  ///
  /// void main() {
  ///   final list = Uint32List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Uint32> get address;
}

@Since('3.5')
extension Uint64ListAddress on Uint64List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Uint64>)>(isLeaf: true)
  /// external void myFunction(Pointer<Uint64> pointer);
  ///
  /// void main() {
  ///   final list = Uint64List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Uint64> get address;
}

@Since('3.5')
extension Float32ListAddress on Float32List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
  /// external void myFunction(Pointer<Float> pointer);
  ///
  /// void main() {
  ///   final list = Float32List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Float> get address;
}

@Since('3.5')
extension Float64ListAddress on Float64List {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address`
  /// can only occurr as an entire argument expression in the invocation of
  /// a leaf [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Double>)>(isLeaf: true)
  /// external void myFunction(Pointer<Double> pointer);
  ///
  /// void main() {
  ///   final list = Float64List(10);
  ///   myFunction(list.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Double> get address;
}

//
// End of generated code.
//

/// Extension on [Pointer] specialized for the type argument [Pointer].
extension PointerPointer<T extends NativeType> on Pointer<Pointer<T>> {
  /// The pointer at [address].
  ///
  /// A [Pointer] is unboxed before being stored (as if by `.address`), and the
  /// pointer is boxed (as if by `Pointer.fromAddress`) when loaded.
  ///
  /// On 32-bit platforms the [address] must be 4-byte aligned, and on 64-bit
  /// platforms the [address] must be 8-byte aligned.
  external Pointer<T> get value;

  external void set value(Pointer<T> value);

  /// Load a Dart value from this location offset by [index].
  ///
  /// A [Pointer] is unboxed before being stored (as if by `.address`), and the
  /// pointer is boxed (as if by `Pointer.fromAddress`) when loaded.
  ///
  /// On 32-bit platforms the [address] must be 4-byte aligned, and on 64-bit
  /// platforms the [address] must be 8-byte aligned.
  external Pointer<T> operator [](int index);

  /// Store a Dart value into this location offset by [index].
  ///
  /// A [Pointer] is unboxed before being stored (as if by `.address`), and the
  /// pointer is boxed (as if by [Pointer.fromAddress]) when loaded.
  ///
  /// On 32-bit platforms the [address] must be 4-byte aligned, and on 64-bit
  /// platforms the [address] must be 8-byte aligned.
  external void operator []=(int index, Pointer<T> value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  external Pointer<Pointer<T>> elementAt(int index);

  /// A pointer to the [offset]th [Pointer<T>] after this one.
  ///
  /// Returns a pointer to the [Pointer<T>] whose address is
  /// [offset] times the size of `Pointer<T>` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<Pointer<T>>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  external Pointer<Pointer<T>> operator +(int offset);

  /// A pointer to the [offset]th [Pointer<T>] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [Pointer<T>] whose address is
  /// [offset] times the size of `Pointer<T>` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<Pointer<T>>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  external Pointer<Pointer<T>> operator -(int offset);
}

/// Extension on [Pointer] specialized for the type argument [Struct].
extension StructPointer<T extends Struct> on Pointer<T> {
  /// A Dart view of the struct referenced by this pointer.
  ///
  /// Reading [ref] creates a reference accessing the fields of this struct
  /// backed by native memory at [address].
  /// The [address] must be aligned according to the struct alignment rules of
  /// the platform.
  ///
  /// Assigning to [ref] copies contents of the struct into the native memory
  /// starting at [address].
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T get ref;
  external set ref(T value);

  /// A Dart view of the struct referenced by this pointer.
  ///
  /// Creates a reference accessing the fields of this struct backed by native
  /// memory at [address].
  /// The [address] must be aligned according to the struct alignment rules of
  /// the platform.
  ///
  /// Attaches [finalizer] to the backing store of the struct. If provided,
  /// [token] will be passed to [finalizer], otherwise the pointer (`this`)
  /// itself will be passed.
  /// The pointer (`this`) must _not_ be used anymore if the struct is _not_
  /// guaranteed to be kept alive.
  /// Prefer doing any native calls with the pointer _before_ calling
  /// [refWithFinalizer].
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  @Since('3.7')
  external T refWithFinalizer(
    Pointer<NativeFinalizerFunction> finalizer, {
    Pointer<Void>? token,
  });

  /// Creates a reference to access the fields of this struct backed by native
  /// memory at `address + sizeOf<T>() * index`.
  ///
  /// The [address] must be aligned according to the struct alignment rules of
  /// the platform.
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T operator [](int index);

  /// Copies the [value] struct into native memory, starting at
  /// `address * sizeOf<T>() * index`.
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, T value);

  /// Pointer arithmetic (takes element size into account)
  @Deprecated('Use operator + instead')
  external Pointer<T> elementAt(int index);

  /// A pointer to the [offset]th [T] after this one.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<T>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  external Pointer<T> operator +(int offset);

  /// A pointer to the [offset]th [T] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<T>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  external Pointer<T> operator -(int offset);
}

/// Extension on [Pointer] specialized for the type argument [Union].
@Since('2.14')
extension UnionPointer<T extends Union> on Pointer<T> {
  /// A Dart view of the union referenced by this pointer.
  ///
  /// Reading [ref] creates a reference accessing the fields of this union
  /// backed by native memory at [address].
  /// The [address] must be aligned according to the union alignment rules of
  /// the platform.
  ///
  /// Assigning to [ref] copies contents of the union into the native memory
  /// starting at [address].
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T get ref;
  external set ref(T value);

  /// A Dart view of the union referenced by this pointer.
  ///
  /// Creates a reference accessing the fields of this union backed by native
  /// memory at [address].
  /// The [address] must be aligned according to the union alignment rules of
  /// the platform.
  ///
  /// Attaches [finalizer] to the backing store of the union. If provided,
  /// [token] will be passed to [finalizer], otherwise the pointer (`this`)
  /// itself will be passed.
  /// The pointer (`this`) must _not_ be used anymore if the union is _not_
  /// guaranteed to be kept alive.
  /// Prefer doing any native calls with the pointer _before_ calling
  /// [refWithFinalizer].
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  @Since('3.7')
  external T refWithFinalizer(
    Pointer<NativeFinalizerFunction> finalizer, {
    Pointer<Void>? token,
  });

  /// Creates a reference to access the fields of this union backed by native
  /// memory at `address + sizeOf<T>() * index`.
  ///
  /// The [address] must be aligned according to the union alignment rules of
  /// the platform.
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T operator [](int index);

  /// Copies the [value] union into native memory, starting at
  /// `address * sizeOf<T>() * index`.
  ///
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, T value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  external Pointer<T> elementAt(int index);

  /// A pointer to the [offset]th [T] after this one.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<T>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  external Pointer<T> operator +(int offset);

  /// A pointer to the [offset]th [T] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<T>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  external Pointer<T> operator -(int offset);
}

/// Extension on [Pointer] specialized for the type argument
/// [AbiSpecificInteger].
@Since('2.16')
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
    on Pointer<T> {
  /// The integer at [address].
  external int get value;

  external void set value(int value);

  /// The integer at `address + sizeOf<T>() * index`.
  external int operator [](int index);

  /// The integer at `address + sizeOf<T>() * index`.
  external void operator []=(int index, int value);

  /// Pointer arithmetic (takes element size into account).
  @Deprecated('Use operator + instead')
  external Pointer<T> elementAt(int index);

  /// A pointer to the [offset]th [T] after this one.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` after the address of this pointer.
  /// That is `(this + offset).address == this.address + offset * sizeOf<T>()`.
  ///
  /// Also `(this + offset).value` is equivalent to `this[offset]`,
  /// and similarly for setting.
  external Pointer<T> operator +(int offset);

  /// A pointer to the [offset]th [T] before this one.
  ///
  /// Equivalent to `this + (-offset)`.
  ///
  /// Returns a pointer to the [T] whose address is
  /// [offset] times the size of `T` before the address of this pointer.
  /// That is, `(this - offset).address == this.address - offset * sizeOf<T>()`.
  ///
  /// Also, `(this - offset).value` is equivalent to `this[-offset]`,
  /// and similarly for setting,
  external Pointer<T> operator -(int offset);
}

/// Bounds checking indexing methods on [Array]s of [Pointer].
@Since('2.13')
extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external Pointer<T> operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, Pointer<T> value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external List<Pointer<T>> get elements;
}

/// Bounds checking indexing methods on [Array]s of [Struct].
@Since('2.13')
extension StructArray<T extends Struct> on Array<T> {
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T operator [](int index);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array.
  @Since('3.8')
  external List<T> get elements;
}

/// Bounds checking indexing methods on [Array]s of [Union].
@Since('2.14')
extension UnionArray<T extends Union> on Array<T> {
  /// This extension method must be invoked on a receiver of type `Pointer<T>`
  /// where `T` is a compile-time constant type.
  external T operator [](int index);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array.
  @Since('3.8')
  external List<T> get elements;
}

/// Bounds checking indexing methods on [Array]s of [Array].
@Since('2.13')
extension ArrayArray<T extends NativeType> on Array<Array<T>> {
  external Array<T> operator [](int index);

  external void operator []=(int index, Array<T> value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external List<Array<T>> get elements;
}

/// Bounds checking indexing methods on [Array]s of [AbiSpecificInteger].
@Since('2.16')
extension AbiSpecificIntegerArray<T extends AbiSpecificInteger> on Array<T> {
  /// Loads a Dart value from this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external int operator [](int index);

  /// Stores a Dart value in this array at [index].
  ///
  /// This extension method must be invoked on a receiver of type `Array<T>`
  /// where `T` is a compile-time constant type.
  external void operator []=(int index, int value);

  /// A list view of the bytes of this array.
  ///
  /// Has the same length and elements (as accessed using the index operator)
  /// as this array, and writes to either the list or this arrary are visible
  /// in both.
  @Since('3.8')
  external List<int> get elements;
}

@Since('3.5')
extension ArrayAddress<T extends NativeType> on Array<T> {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart
  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int8> pointer);
  ///
  /// final class MyStruct extends Struct {
  ///   @Array(10)
  ///   external Array<Int8> array;
  /// }
  ///
  /// void main() {
  ///   final array = Struct.create<MyStruct>().array;
  ///   myFunction(array.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<T> get address;
}

@Since('3.5')
extension StructAddress<T extends Struct> on T {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart
  /// @Native<Void Function(Pointer<MyStruct>)>(isLeaf: true)
  /// external void myFunction(Pointer<MyStruct> pointer);
  ///
  /// final class MyStruct extends Struct {
  ///   @Int8()
  ///   external int x;
  /// }
  ///
  /// void main() {
  ///   final myStruct = Struct.create<MyStruct>();
  ///   myFunction(myStruct.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<T> get address;
}

@Since('3.5')
extension UnionAddress<T extends Union> on T {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Example:
  ///
  /// ```dart
  /// @Native<Void Function(Pointer<MyUnion>)>(isLeaf: true)
  /// external void myFunction(Pointer<MyUnion> pointer);
  ///
  /// final class MyUnion extends Union {
  ///   @Int8()
  ///   external int x;
  /// }
  ///
  /// void main() {
  ///   final myUnion = Union.create<MyUnion>();
  ///   myFunction(myUnion.address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<T> get address;
}

@Since('3.5')
extension IntAddress on int {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
  /// subtypes, [Array] elements, or [TypedData] elements. In other words, the
  /// number whose address is being accessed must itself be acccessed through a
  /// [Struct], [Union], [Array], or [TypedData].
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int8> pointer);
  ///
  /// final class MyStruct extends Struct {
  ///   @Int8()
  ///   external int x;
  ///
  ///   @Int8()
  ///   external int y;
  ///
  ///   @Array(10)
  ///   external Array<Int8> array;
  /// }
  ///
  /// void main() {
  ///   final myStruct = Struct.create<MyStruct>();
  ///   myFunction(myStruct.y.address);
  ///   myFunction(myStruct.array[5].address);
  ///
  ///   final list = Int8List(10);
  ///   myFunction(list[5].address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Never> get address;
}

@Since('3.5')
extension DoubleAddress on double {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
  /// subtypes, [Array] elements, or [TypedData] elements. In other words, the
  /// number whose address is being accessed must itself be acccessed through a
  /// [Struct], [Union], [Array], or [TypedData].
  ///
  /// Example:
  ///
  /// ```dart import:typed_data
  /// @Native<Void Function(Pointer<Float>)>(isLeaf: true)
  /// external void myFunction(Pointer<Float> pointer);
  ///
  /// final class MyStruct extends Struct {
  ///   @Float()
  ///   external double x;
  ///
  ///   @Float()
  ///   external double y;
  ///
  ///   @Array(10)
  ///   external Array<Float> array;
  /// }
  ///
  /// void main() {
  ///   final myStruct = Struct.create<MyStruct>();
  ///   myFunction(myStruct.y.address);
  ///   myFunction(myStruct.array[5].address);
  ///
  ///   final list = Float32List(10);
  ///   myFunction(list[5].address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Never> get address;
}

@Since('3.5')
extension BoolAddress on bool {
  /// The memory address of the underlying data.
  ///
  /// An expression of the form `expression.address` denoting this `address` can
  /// only occurr as an entire argument expression in the invocation of a leaf
  /// [Native] external function.
  ///
  /// Can only be used on fields of [Struct] subtypes, fields of [Union]
  /// subtypes, or [Array] elements. In other words, the boolean whose address
  /// is being accessed must itself be acccessed through a [Struct], [Union] or
  /// [Array].
  ///
  /// Example:
  ///
  /// ```dart
  /// @Native<Void Function(Pointer<Int8>)>(isLeaf: true)
  /// external void myFunction(Pointer<Int8> pointer);
  ///
  /// final class MyStruct extends Struct {
  ///   @Bool()
  ///   external bool x;
  ///
  ///   @Bool()
  ///   external bool y;
  ///
  ///   @Array(10)
  ///   external Array<Bool> array;
  /// }
  ///
  /// void main() {
  ///   final myStruct = Struct.create<MyStruct>();
  ///   myFunction(myStruct.y.address);
  ///   myFunction(myStruct.array[5].address);
  /// }
  /// ```
  ///
  /// The expression before `.address` is evaluated like the left-hand-side of
  /// an assignment, to something that gives access to the storage behind the
  /// expression, which can be used both for reading and writing. The `.address`
  /// then gives a native pointer to that storage.
  ///
  /// The `.address` is evaluated just before calling into native code when
  /// invoking a leaf [Native] external function. This ensures the Dart garbage
  /// collector will not move the object that the address points in to.
  external Pointer<Never> get address;
}

/// Extension to retrieve the native `Dart_Port` from a [SendPort].
extension NativePort on SendPort {
  /// The native port of this [SendPort].
  ///
  /// The returned native port can for example be used by C code to post
  /// messages to the connected [ReceivePort] via `Dart_PostCObject()` - see
  /// `dart_native_api.h`.
  ///
  /// Only the send ports from the platform classes [ReceivePort] and
  /// [RawReceivePort] are supported. User-defined implementations of
  /// [SendPort] are not supported.
  external int get nativePort;
}

/// Opaque, not exposing it's members.
final class Dart_CObject extends Opaque {}

typedef Dart_NativeMessageHandler = Void Function(Int64, Pointer<Dart_CObject>);

/// Utilities for accessing the Dart VM API from Dart code or
/// from C code via `dart_api_dl.h`.
abstract final class NativeApi {
  /// On breaking changes the major version is increased.
  ///
  /// The versioning covers the API surface in `dart_api_dl.h`.
  external static int get majorVersion;

  /// On backwards compatible changes the minor version is increased.
  ///
  /// The versioning covers the API surface in `dart_api_dl.h`.
  external static int get minorVersion;

  /// A function pointer to
  /// `bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message)`
  /// in `dart_native_api.h`.
  external static Pointer<
    NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>
  >
  get postCObject;

  /// A function pointer to
  /// ```c
  /// Dart_Port Dart_NewNativePort(const char* name,
  ///                              Dart_NativeMessageHandler handler,
  ///                              bool handle_concurrently)
  /// ```
  /// in `dart_native_api.h`.
  external static Pointer<
    NativeFunction<
      Int64 Function(
        Pointer<Uint8>,
        Pointer<NativeFunction<Dart_NativeMessageHandler>>,
        Int8,
      )
    >
  >
  get newNativePort;

  /// A function pointer to
  /// `bool Dart_CloseNativePort(Dart_Port native_port_id)`
  /// in `dart_native_api.h`.
  external static Pointer<NativeFunction<Int8 Function(Int64)>>
  get closeNativePort;

  /// Pass this to `Dart_InitializeApiDL` in your native code to enable using the
  /// symbols in `dart_api_dl.h`.
  external static Pointer<Void> get initializeApiDLData;
}

/// Annotation binding an external declaration to its native implementation.
///
/// Can only be applied to `external` declarations of static and top-level
/// functions and variables.
///
/// A [Native]-annotated `external` function is implemented by native code.
/// The implementation is found in the native library denoted by [assetId].
/// Similarly, a [Native]-annotated `external` variable is implemented by
/// reading from or writing to native memory.
///
/// The compiler and/or runtime provides a binding from [assetId] to native
/// library, which depends on the target platform.
/// The compiler/runtime can then resolve/lookup symbols (identifiers)
/// against the native library, to find a native function or a native global
/// variable, and bind an `external` Dart function or variable declaration to
/// that native declaration.
/// By default, the runtime expects a native symbol with the same name as the
/// annotated function or variable in Dart. This can be overridden with the
/// [symbol] parameter on the annotation.
///
/// When used on a function, [T] must be a function type that represents the
/// native function's parameter and return types. The parameter and return types
/// must be subtypes of [NativeType].
///
/// When used on a variable, [T] must be a compatible native type. For example,
/// an [int] field can be annotated with [Int32].
///
/// If the type argument [T] is omitted in the `@Native` annotation, it is
/// inferred from the static type of the declaration, which must meet the
/// following constraints:
///
/// For function or method declarations:
/// - The return type must be one of the following:
///   - [Pointer]
///   - `void`
///   - Subtype of compound types, such as [Struct] or [Union]
/// - The parameter types must be subtypes of compound types or [Pointer]
///
/// For variable declarations, the type can be any of the following:
///   - [Pointer]
///   - Subtype of compound types, such as [Struct] or [Union]
///
/// For native global variables that cannot be reassigned, a `final` variable in
/// Dart or a getter can be used to prevent modifications to the native field.
///
/// Example:
///
/// ```dart template:top
/// @Native<Int64 Function(Int64, Int64)>()
/// external int sum(int a, int b);
///
/// @Native()
/// external void free(Pointer p);
///
/// @Native<Int64>()
/// external int aGlobalInt;
///
/// @Native()
/// external final Pointer<Char> aGlobalString;
/// ```
///
/// Calling a `@Native` function, as well as reading or writing to a `@Native`
/// variable, will try to resolve the [symbol] in (in the order):
/// 1. the provided or default [assetId],
/// 2. the native resolver set with `Dart_SetFfiNativeResolver` in
///    `dart_api.h`, and
/// 3. the current process.
///
/// At least one of those three *must* provide a binding for the symbol,
/// otherwise the method call or the variable access fails.
///
/// NOTE: This is an experimental feature and may change in the future.
@Since('2.19')
final class Native<T> {
  // Implementation note: VM hardcodes the layout of this class (number and
  // order of its fields), so adding/removing/changing fields requires
  // updating the VM code (see Function::GetNativeAnnotation()).

  /// The native symbol to be resolved, if not using the default.
  ///
  /// If not specified, the default symbol used for native function lookup
  /// is the annotated function's name.
  ///
  /// Example:
  ///
  /// ```dart template:top
  /// @Native<Int64 Function(Int64, Int64)>()
  /// external int sum(int a, int b);
  /// ```
  ///
  /// Example 2:
  ///
  /// ```dart template:top
  /// @Native<Int64 Function(Int64, Int64)>(symbol: 'sum')
  /// external int sum(int a, int b);
  /// ```
  ///
  /// The above two examples are equivalent.
  ///
  /// Prefer omitting the [symbol] when possible.
  final String? symbol;

  /// The ID of the asset in which [symbol] is resolved, if not using the
  /// default.
  ///
  /// If no asset name is specified, the default is to use an asset ID
  /// specified using an [DefaultAsset] annotation on the current library's
  /// `library` declaration, and if there is no [DefaultAsset] annotation on
  /// the current library, the library's URI (as a string) is used instead.
  ///
  /// Example (file `package:a/a.dart`):
  ///
  /// ```dart template:top
  /// @Native<Int64 Function(Int64, Int64)>()
  /// external int sum(int a, int b);
  /// ```
  ///
  /// Example 2 (file `package:a/a.dart`):
  ///
  /// ```dart template:none
  /// @DefaultAsset('package:a/a.dart')
  /// library a;
  ///
  /// import 'dart:ffi';
  ///
  /// @Native<Int64 Function(Int64, Int64)>()
  /// external int sum(int a, int b);
  /// ```
  ///
  /// Example 3 (file `package:a/a.dart`):
  ///
  /// ```dart template:top
  /// @Native<Int64 Function(Int64, Int64)>(assetId: 'package:a/a.dart')
  /// external int sum(int a, int b);
  /// ```
  ///
  /// The above three examples are all equivalent.
  ///
  /// Prefer using the library URI as an asset name over specifying it.
  /// Prefer using an [DefaultAsset] on the `library` declaration
  /// over specifying the asset name in a [Native] annotation.
  final String? assetId;

  /// Whether the function is a leaf function.
  ///
  /// Leaf functions are small, short-running, non-blocking functions which are
  /// not allowed to call back into Dart or use any Dart VM APIs. Leaf functions
  /// are invoked bypassing some of the heavier parts of the standard
  /// Dart-to-Native calling sequence which reduces the invocation overhead,
  /// making leaf calls faster than non-leaf calls. However, this implies that a
  /// thread executing a leaf function can't cooperate with the Dart runtime. A
  /// long running or blocking leaf function will delay any operation which
  /// requires synchronization between all threads associated with an isolate
  /// group until after the leaf function returns. For example, if one isolate
  /// in a group is trying to perform a GC and a second isolate is blocked in a
  /// leaf call, then the first isolate will have to pause and wait until this
  /// leaf call returns.
  ///
  /// This value has no meaning for native fields.
  final bool isLeaf;

  const Native({this.assetId, this.isLeaf = false, this.symbol});

  /// The native address of the implementation of [native].
  ///
  /// When calling this function, the argument for [native] must be an
  /// expression denoting a variable or function declaration which is annotated
  /// with [Native].
  /// For a variable declaration, the type [T] must be the same native type
  /// as the type argument to that `@Native` annotation.
  /// For a function declaration, the type [T] must be `NativeFunction<F>`
  /// where `F` was the type argument to that `@Native` annotation.
  ///
  /// For example, for a native C library exposing a function:
  ///
  /// ```C
  /// #include <stdint.h>
  /// int64_t sum(int64_t a, int64_t b) { return a + b; }
  /// ```
  ///
  /// The following code binds `sum` to a Dart function declaration, and
  /// extracts the address of the native `sum` implementation:
  ///
  /// ```dart
  /// import 'dart:ffi';
  ///
  /// typedef NativeAdd = Int64 Function(Int64, Int64);
  ///
  /// @Native<NativeAdd>()
  /// external int sum(int a, int b);
  ///
  /// void main() {
  ///   Pointer<NativeFunction<NativeAdd>> addressSum = Native.addressOf(sum);
  /// }
  /// ```
  ///
  /// Similarly, for a native C library exposing a global variable:
  ///
  /// ```C
  /// const char* myString;
  /// ```
  ///
  /// The following code binds `myString` to a top-level variable in Dart, and
  /// extracts the address of the underlying native field:
  ///
  /// ```dart
  /// import 'dart:ffi';
  ///
  /// @Native()
  /// external Pointer<Char> myString;
  ///
  /// void main() {
  ///   // This pointer points to the memory location where the loader has
  ///   // placed the `myString` global itself. To get the string value, read
  ///   // the myString field directly.
  ///   Pointer<Pointer<Char>> addressMyString = Native.addressOf(myString);
  /// }
  /// ```
  @Since('3.3')
  external static Pointer<T> addressOf<T extends NativeType>(
    @DartRepresentationOf('T') Object native,
  );
}

/// Annotation specifying the default asset ID for the current library.
///
/// The annotation applies only to `library` declarations.
///
/// The compiler and/or runtime provides a binding from _asset ID_ to native
/// library, which depends on the target platform and architecture.
/// The compiler/runtime can resolve identifiers (symbols)
/// against the native library, looking up native function implementations
/// which are then used as the implementation of `external` Dart function
/// declarations.
///
/// If used as annotation on a `library` declaration, all [Native]-annotated
/// external functions in this library will use the specified asset [id]
/// for native function resolution (unless overridden by [Native.assetId]).
///
/// If no [DefaultAsset] annotation is provided, the current library's URI
/// is the default asset ID for [Native]-annotated external functions.
///
/// Example (file `package:a/a.dart`):
///
/// ```dart template:top
/// @Native<Int64 Function(Int64, Int64)>()
/// external int sum(int a, int b);
/// ```
///
/// Example 2 (file `package:a/a.dart`):
///
/// ```dart template:none
/// @DefaultAsset('package:a/a.dart')
/// library a;
///
/// import 'dart:ffi';
///
/// @Native<Int64 Function(Int64, Int64)>()
/// external int sum(int a, int b);
/// ```
///
/// The above two examples are equivalent.
///
/// Prefer using the library URI as asset name when possible.
///
/// NOTE: This is an experimental feature and may change in the future.
@Since('2.19')
final class DefaultAsset {
  /// The default asset name for [Native] external functions in this library.
  final String id;

  const DefaultAsset(this.id);
}
