/**
 * `bun:ffi` lets you efficiently call C functions & FFI functions from JavaScript
 *  without writing bindings yourself.
 *
 * ```js
 * import {dlopen, CString, ptr} from 'bun:ffi';
 *
 * const lib = dlopen('libsqlite3', {
 * });
 * ```
 *
 * This is powered by just-in-time compiling C wrappers
 * that convert JavaScript types to C types and back. Internally,
 * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
 * goes to Fabrice Bellard and TinyCC maintainers for making this possible.
 */
declare module "bun:ffi" {
	enum FFIType {
		char = 0,
		/**
		 * 8-bit signed integer
		 *
		 * Must be a value between -127 and 127
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * signed char
		 * char // on x64 & aarch64 macOS
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		int8_t = 1,
		/**
		 * 8-bit signed integer
		 *
		 * Must be a value between -127 and 127
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * signed char
		 * char // on x64 & aarch64 macOS
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		i8 = 1,

		/**
		 * 8-bit unsigned integer
		 *
		 * Must be a value between 0 and 255
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * unsigned char
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		uint8_t = 2,
		/**
		 * 8-bit unsigned integer
		 *
		 * Must be a value between 0 and 255
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * unsigned char
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		u8 = 2,

		/**
		 * 16-bit signed integer
		 *
		 * Must be a value between -32768 and 32767
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * in16_t
		 * short // on arm64 & x64
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		int16_t = 3,
		/**
		 * 16-bit signed integer
		 *
		 * Must be a value between -32768 and 32767
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * in16_t
		 * short // on arm64 & x64
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		i16 = 3,

		/**
		 * 16-bit unsigned integer
		 *
		 * Must be a value between 0 and 65535, inclusive.
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * uint16_t
		 * unsigned short // on arm64 & x64
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		uint16_t = 4,
		/**
		 * 16-bit unsigned integer
		 *
		 * Must be a value between 0 and 65535, inclusive.
		 *
		 * When passing to a FFI function (C ABI), type coercion is not performed.
		 *
		 * In C:
		 * ```c
		 * uint16_t
		 * unsigned short // on arm64 & x64
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * var num = 0;
		 * ```
		 */
		u16 = 4,

		/**
		 * 32-bit signed integer
		 */
		int32_t = 5,

		/**
		 * 32-bit signed integer
		 *
		 * Alias of {@link FFIType.int32_t}
		 */
		i32 = 5,
		/**
		 * 32-bit signed integer
		 *
		 * The same as `int` in C
		 *
		 * ```c
		 * int
		 * ```
		 */
		int = 5,

		/**
		 * 32-bit unsigned integer
		 *
		 * The same as `unsigned int` in C (on x64 & arm64)
		 *
		 * C:
		 * ```c
		 * unsigned int
		 * ```
		 * JavaScript:
		 * ```js
		 * ptr(new Uint32Array(1))
		 * ```
		 */
		uint32_t = 6,
		/**
		 * 32-bit unsigned integer
		 *
		 * Alias of {@link FFIType.uint32_t}
		 */
		u32 = 6,

		/**
		 * int64 is a 64-bit signed integer
		 *
		 * This is not implemented yet!
		 */
		int64_t = 7,
		/**
		 * i64 is a 64-bit signed integer
		 *
		 * This is not implemented yet!
		 */
		i64 = 7,

		/**
		 * 64-bit unsigned integer
		 *
		 * This is not implemented yet!
		 */
		uint64_t = 8,
		/**
		 * 64-bit unsigned integer
		 *
		 * This is not implemented yet!
		 */
		u64 = 8,

		/**
		 * Doubles are not supported yet!
		 */
		double = 9,
		/**
		 * Doubles are not supported yet!
		 */
		f64 = 9,
		/**
		 * Floats are not supported yet!
		 */
		float = 10,
		/**
		 * Floats are not supported yet!
		 */
		f32 = 10,

		/**
		 * Boolean value
		 *
		 * Must be `true` or `false`. `0` and `1` type coercion is not supported.
		 *
		 * In C, this corresponds to:
		 * ```c
		 * bool
		 * _Bool
		 * ```
		 */
		bool = 11,

		/**
		 * Pointer value
		 *
		 * See {@link Bun.FFI.ptr} for more information
		 *
		 * In C:
		 * ```c
		 * void*
		 * ```
		 *
		 * In JavaScript:
		 * ```js
		 * ptr(new Uint8Array(1))
		 * ```
		 */
		ptr = 12,
		/**
		 * Pointer value
		 *
		 * alias of {@link FFIType.ptr}
		 */
		pointer = 12,

		/**
		 * void value
		 *
		 * void arguments are not supported
		 *
		 * void return type is the default return type
		 *
		 * In C:
		 * ```c
		 * void
		 * ```
		 */
		void = 13,

		/**
		 * When used as a `returns`, this will automatically become a {@link CString}.
		 *
		 * When used in `args` it is equivalent to {@link FFIType.pointer}
		 */
		cstring = 14,

		/**
		 * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance
		 * but means you might get a `BigInt` or you might get a `number`.
		 *
		 * In C, this always becomes `int64_t`
		 *
		 * In JavaScript, this could be number or it could be BigInt, depending on what
		 * value is passed in.
		 */
		i64_fast = 15,

		/**
		 * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance
		 * but means you might get a `BigInt` or you might get a `number`.
		 *
		 * In C, this always becomes `uint64_t`
		 *
		 * In JavaScript, this could be number or it could be BigInt, depending on what
		 * value is passed in.
		 */
		u64_fast = 16,
		function = 17,
	}

	type Pointer = number & { __pointer__: null };

	interface FFITypeToArgsType {
		[FFIType.char]: number;
		[FFIType.int8_t]: number;
		[FFIType.i8]: number;
		[FFIType.uint8_t]: number;
		[FFIType.u8]: number;
		[FFIType.int16_t]: number;
		[FFIType.i16]: number;
		[FFIType.uint16_t]: number;
		[FFIType.u16]: number;
		[FFIType.int32_t]: number;
		[FFIType.i32]: number;
		[FFIType.int]: number;
		[FFIType.uint32_t]: number;
		[FFIType.u32]: number;
		[FFIType.int64_t]: number | bigint;
		[FFIType.i64]: number | bigint;
		[FFIType.uint64_t]: number | bigint;
		[FFIType.u64]: number | bigint;
		[FFIType.double]: number;
		[FFIType.f64]: number;
		[FFIType.float]: number;
		[FFIType.f32]: number;
		[FFIType.bool]: boolean;
		[FFIType.ptr]: NodeJS.TypedArray | Pointer | CString | null;
		[FFIType.pointer]: NodeJS.TypedArray | Pointer | CString | null;
		[FFIType.void]: undefined;
		[FFIType.cstring]: NodeJS.TypedArray | Pointer | CString | null;
		[FFIType.i64_fast]: number | bigint;
		[FFIType.u64_fast]: number | bigint;
		[FFIType.function]: Pointer | JSCallback; // cannot be null
	}
	interface FFITypeToReturnsType {
		[FFIType.char]: number;
		[FFIType.int8_t]: number;
		[FFIType.i8]: number;
		[FFIType.uint8_t]: number;
		[FFIType.u8]: number;
		[FFIType.int16_t]: number;
		[FFIType.i16]: number;
		[FFIType.uint16_t]: number;
		[FFIType.u16]: number;
		[FFIType.int32_t]: number;
		[FFIType.i32]: number;
		[FFIType.int]: number;
		[FFIType.uint32_t]: number;
		[FFIType.u32]: number;
		[FFIType.int64_t]: bigint;
		[FFIType.i64]: bigint;
		[FFIType.uint64_t]: bigint;
		[FFIType.u64]: bigint;
		[FFIType.double]: number;
		[FFIType.f64]: number;
		[FFIType.float]: number;
		[FFIType.f32]: number;
		[FFIType.bool]: boolean;
		[FFIType.ptr]: Pointer | null;
		[FFIType.pointer]: Pointer | null;
		[FFIType.void]: undefined;
		[FFIType.cstring]: CString;
		[FFIType.i64_fast]: number | bigint;
		[FFIType.u64_fast]: number | bigint;
		[FFIType.function]: Pointer | null;
	}
	interface FFITypeStringToType {
		["char"]: FFIType.char;
		["int8_t"]: FFIType.int8_t;
		["i8"]: FFIType.i8;
		["uint8_t"]: FFIType.uint8_t;
		["u8"]: FFIType.u8;
		["int16_t"]: FFIType.int16_t;
		["i16"]: FFIType.i16;
		["uint16_t"]: FFIType.uint16_t;
		["u16"]: FFIType.u16;
		["int32_t"]: FFIType.int32_t;
		["i32"]: FFIType.i32;
		["int"]: FFIType.int;
		["uint32_t"]: FFIType.uint32_t;
		["u32"]: FFIType.u32;
		["int64_t"]: FFIType.int64_t;
		["i64"]: FFIType.i64;
		["uint64_t"]: FFIType.uint64_t;
		["u64"]: FFIType.u64;
		["double"]: FFIType.double;
		["f64"]: FFIType.f64;
		["float"]: FFIType.float;
		["f32"]: FFIType.f32;
		["bool"]: FFIType.bool;
		["ptr"]: FFIType.ptr;
		["pointer"]: FFIType.pointer;
		["void"]: FFIType.void;
		["cstring"]: FFIType.cstring;
		["function"]: FFIType.pointer; // for now
		["usize"]: FFIType.uint64_t; // for now
		["callback"]: FFIType.pointer; // for now
	}

	type FFITypeOrString = FFIType | keyof FFITypeStringToType;

	interface FFIFunction {
		/**
		 * Arguments to a FFI function (C ABI)
		 *
		 * Defaults to an empty array, which means no arguments.
		 *
		 * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}.
		 *
		 * @example
		 * From JavaScript:
		 * ```ts
		 * import { dlopen, FFIType, suffix } from "bun:ffi"
		 *
		 * const lib = dlopen(`adder.${suffix}`, {
		 * 	add: {
		 * 		// FFIType can be used or you can pass string labels.
		 * 		args: [FFIType.i32, "i32"],
		 * 		returns: "i32",
		 * 	},
		 * })
		 * lib.symbols.add(1, 2)
		 * ```
		 * In C:
		 * ```c
		 * int add(int a, int b) {
		 *   return a + b;
		 * }
		 * ```
		 */
		readonly args?: readonly FFITypeOrString[];
		/**
		 * Return type to a FFI function (C ABI)
		 *
		 * Defaults to {@link FFIType.void}
		 *
		 * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}.
		 *
		 * @example
		 * From JavaScript:
		 * ```ts
		 * import { dlopen, CString } from "bun:ffi"
		 *
		 * const lib = dlopen('z', {
		 *    version: {
		 *      returns: "ptr",
		 *   }
		 * });
		 * console.log(new CString(lib.symbols.version()));
		 * ```
		 * In C:
		 * ```c
		 * char* version()
		 * {
		 *  return "1.0.0";
		 * }
		 * ```
		 */
		readonly returns?: FFITypeOrString;

		/**
		 * Function pointer to the native function
		 *
		 * If provided, instead of using dlsym() to lookup the function, Bun will use this instead.
		 * This pointer should not be null (0).
		 *
		 * This is useful if the library has already been loaded
		 * or if the module is also using Node-API.
		 */
		readonly ptr?: Pointer | bigint;

		/**
		 * Can C/FFI code call this function from a separate thread?
		 *
		 * Only supported with {@link JSCallback}.
		 *
		 * This does not make the function run in a separate thread. It is still up to the application/library
		 * to run their code in a separate thread.
		 *
		 * By default, {@link JSCallback} calls are not thread-safe. Turning this on
		 * incurs a small performance penalty for every function call. That small
		 * performance penalty needs to be less than the performance gain from
		 * running the function in a separate thread.
		 *
		 * @default false
		 */
		readonly threadsafe?: boolean;
	}

	type Symbols = Readonly<Record<string, FFIFunction>>;

	// /**
	//  * Compile a callback function
	//  *
	//  * Returns a function pointer
	//  *
	//  */
	// export function callback(ffi: FFIFunction, cb: Function): number;

	interface Library<Fns extends Symbols> {
		symbols: ConvertFns<Fns>;

		/**
		 * `dlclose` the library, unloading the symbols and freeing allocated memory.
		 *
		 * Once called, the library is no longer usable.
		 *
		 * Calling a function from a library that has been closed is undefined behavior.
		 */
		close(): void;
	}

	type ToFFIType<T extends FFITypeOrString> = T extends FFIType
		? T
		: T extends string
			? FFITypeStringToType[T]
			: never;

	type ConvertFns<Fns extends Symbols> = {
		[K in keyof Fns]: (
			...args: Fns[K]["args"] extends infer A extends readonly FFITypeOrString[]
				? { [L in keyof A]: FFITypeToArgsType[ToFFIType<A[L]>] }
				: // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
					[unknown] extends [Fns[K]["args"]]
					? []
					: never
		) => [unknown] extends [Fns[K]["returns"]] // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
			? undefined
			: FFITypeToReturnsType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
	};

	/**
	 * Open a library using `"bun:ffi"`
	 *
	 * @param name The name of the library or file path. This will be passed to `dlopen()`
	 * @param symbols Map of symbols to load where the key is the symbol name and the value is the {@link FFIFunction}
	 *
	 * @example
	 *
	 * ```js
	 * import {dlopen} from 'bun:ffi';
	 *
	 * const lib = dlopen("duckdb.dylib", {
	 *   get_version: {
	 *     returns: "cstring",
	 *     args: [],
	 *   },
	 * });
	 * lib.symbols.get_version();
	 * // "1.0.0"
	 * ```
	 *
	 * This is powered by just-in-time compiling C wrappers
	 * that convert JavaScript types to C types and back. Internally,
	 * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
	 * goes to Fabrice Bellard and TinyCC maintainers for making this possible.
	 */
	function dlopen<Fns extends Record<string, FFIFunction>>(
		name: string | import("bun").BunFile | URL,
		symbols: Fns,
	): Library<Fns>;

	/**
	 * Turn a native library's function pointer into a JavaScript function
	 *
	 * Libraries using Node-API & bun:ffi in the same module could use this to skip an extra dlopen() step.
	 *
	 * @param fn {@link FFIFunction} declaration. `ptr` is required
	 *
	 * @example
	 *
	 * ```js
	 * import {CFunction} from 'bun:ffi';
	 *
	 * const getVersion = new CFunction({
	 *   returns: "cstring",
	 *   args: [],
	 *   ptr: myNativeLibraryGetVersion,
	 * });
	 * getVersion();
	 * getVersion.close();
	 * ```
	 *
	 * This is powered by just-in-time compiling C wrappers
	 * that convert JavaScript types to C types and back. Internally,
	 * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
	 * goes to Fabrice Bellard and TinyCC maintainers for making this possible.
	 */
	function CFunction(fn: FFIFunction & { ptr: Pointer }): CallableFunction & {
		/**
		 * Free the memory allocated by the wrapping function
		 */
		close(): void;
	};

	/**
	 * Link a map of symbols to JavaScript functions
	 *
	 * This lets you use native libraries that were already loaded somehow. You usually will want {@link dlopen} instead.
	 *
	 * You could use this with Node-API to skip loading a second time.
	 *
	 * @param symbols Map of symbols to load where the key is the symbol name and the value is the {@link FFIFunction}
	 *
	 * @example
	 *
	 * ```js
	 * import { linkSymbols } from "bun:ffi";
	 *
	 * const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();
	 *
	 * const lib = linkSymbols({
	 *   // Unlike with dlopen(), the names here can be whatever you want
	 *   getMajor: {
	 *     returns: "cstring",
	 *     args: [],
	 *
	 *     // Since this doesn't use dlsym(), you have to provide a valid ptr
	 *     // That ptr could be a number or a bigint
	 *     // An invalid pointer will crash your program.
	 *     ptr: majorPtr,
	 *   },
	 *   getMinor: {
	 *     returns: "cstring",
	 *     args: [],
	 *     ptr: minorPtr,
	 *   },
	 *   getPatch: {
	 *     returns: "cstring",
	 *     args: [],
	 *     ptr: patchPtr,
	 *   },
	 * });
	 *
	 * const [major, minor, patch] = [
	 *   lib.symbols.getMajor(),
	 *   lib.symbols.getMinor(),
	 *   lib.symbols.getPatch(),
	 * ];
	 * ```
	 *
	 * This is powered by just-in-time compiling C wrappers
	 * that convert JavaScript types to C types and back. Internally,
	 * bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
	 * goes to Fabrice Bellard and TinyCC maintainers for making this possible.
	 */
	function linkSymbols<Fns extends Record<string, FFIFunction>>(
		symbols: Fns,
	): Library<Fns>;

	/**
	 * Read a pointer as a {@link Buffer}
	 *
	 * If `byteLength` is not provided, the pointer is assumed to be 0-terminated.
	 *
	 * @param ptr The memory address to read
	 * @param byteOffset bytes to skip before reading
	 * @param byteLength bytes to read
	 *
	 * While there are some checks to catch invalid pointers, this is a difficult
	 * thing to do safely. Passing an invalid pointer can crash the program and
	 * reading beyond the bounds of the pointer will crash the program or cause
	 * undefined behavior. Use with care!
	 */
	function toBuffer(
		ptr: Pointer,
		byteOffset?: number,
		byteLength?: number,
	): Buffer;

	/**
	 * Read a pointer as an {@link ArrayBuffer}
	 *
	 * If `byteLength` is not provided, the pointer is assumed to be 0-terminated.
	 *
	 * @param ptr The memory address to read
	 * @param byteOffset bytes to skip before reading
	 * @param byteLength bytes to read
	 *
	 * While there are some checks to catch invalid pointers, this is a difficult
	 * thing to do safely. Passing an invalid pointer can crash the program and
	 * reading beyond the bounds of the pointer will crash the program or cause
	 * undefined behavior. Use with care!
	 */
	function toArrayBuffer(
		ptr: Pointer,
		byteOffset?: number,
		byteLength?: number,
	): ArrayBuffer;

	namespace read {
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function u8(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function i8(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function u16(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function i16(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function u32(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function i32(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function f32(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function u64(ptr: Pointer, byteOffset?: number): bigint;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function i64(ptr: Pointer, byteOffset?: number): bigint;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function f64(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function ptr(ptr: Pointer, byteOffset?: number): number;
		/**
		 * The read function behaves similarly to DataView,
		 * but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
		 *
		 * @param ptr The memory address to read
		 * @param byteOffset bytes to skip before reading
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		function intptr(ptr: Pointer, byteOffset?: number): number;
	}

	/**
	 * Get the pointer backing a {@link TypedArray} or {@link ArrayBuffer}
	 *
	 * Use this to pass {@link TypedArray} or {@link ArrayBuffer} to C functions.
	 *
	 * This is for use with FFI functions. For performance reasons, FFI will
	 * not automatically convert typed arrays to C pointers.
	 *
	 * @param {TypedArray|ArrayBuffer|DataView} view the typed array or array buffer to get the pointer for
	 * @param {number} byteOffset optional offset into the view in bytes
	 *
	 * @example
	 *
	 * From JavaScript:
	 * ```js
	 * const array = new Uint8Array(10);
	 * const rawPtr = ptr(array);
	 * myFFIFunction(rawPtr);
	 * ```
	 * To C:
	 * ```c
	 * void myFFIFunction(char* rawPtr) {
	 *  // Do something with rawPtr
	 * }
	 * ```
	 */
	function ptr(
		view: NodeJS.TypedArray | ArrayBufferLike | DataView,
		byteOffset?: number,
	): Pointer;

	/**
	 * Get a string from a UTF-8 encoded C string
	 * If `byteLength` is not provided, the string is assumed to be null-terminated.
	 *
	 * @example
	 * ```js
	 * var ptr = lib.symbols.getVersion();
	 * console.log(new CString(ptr));
	 * ```
	 *
	 * @example
	 * ```js
	 * var ptr = lib.symbols.getVersion();
	 * // print the first 4 characters
	 * console.log(new CString(ptr, 0, 4));
	 * ```
	 *
	 * While there are some checks to catch invalid pointers, this is a difficult
	 * thing to do safely. Passing an invalid pointer can crash the program and
	 * reading beyond the bounds of the pointer will crash the program or cause
	 * undefined behavior. Use with care!
	 */

	class CString extends String {
		/**
		 * Get a string from a UTF-8 encoded C string
		 * If `byteLength` is not provided, the string is assumed to be null-terminated.
		 *
		 * @param ptr The pointer to the C string
		 * @param byteOffset bytes to skip before reading
		 * @param byteLength bytes to read
		 *
		 * @example
		 * ```js
		 * var ptr = lib.symbols.getVersion();
		 * console.log(new CString(ptr));
		 * ```
		 *
		 * @example
		 * ```js
		 * var ptr = lib.symbols.getVersion();
		 * // print the first 4 characters
		 * console.log(new CString(ptr, 0, 4));
		 * ```
		 *
		 * While there are some checks to catch invalid pointers, this is a difficult
		 * thing to do safely. Passing an invalid pointer can crash the program and
		 * reading beyond the bounds of the pointer will crash the program or cause
		 * undefined behavior. Use with care!
		 */
		constructor(ptr: Pointer, byteOffset?: number, byteLength?: number);

		/**
		 * The ptr to the C string
		 *
		 * This `CString` instance is a clone of the string, so it
		 * is safe to continue using this instance after the `ptr` has been
		 * freed.
		 */
		ptr: Pointer;
		byteOffset?: number;
		byteLength?: number;

		/**
		 * Get the {@link ptr} as an `ArrayBuffer`
		 *
		 * `null` or empty ptrs returns an `ArrayBuffer` with `byteLength` 0
		 */
		get arrayBuffer(): ArrayBuffer;
	}

	/**
	 * Pass a JavaScript function to FFI (Foreign Function Interface)
	 */
	class JSCallback {
		/**
		 * Enable a JavaScript callback function to be passed to C with bun:ffi
		 *
		 * @param callback The JavaScript function to be called
		 * @param definition The C function definition
		 */
		constructor(callback: (...args: any[]) => any, definition: FFIFunction);

		/**
		 * The pointer to the C function
		 *
		 * Becomes `null` once {@link JSCallback.prototype.close} is called
		 */
		readonly ptr: Pointer | null;

		/**
		 * Can the callback be called from a different thread?
		 */
		readonly threadsafe: boolean;

		/**
		 * Free the memory allocated for the callback
		 *
		 * If called multiple times, does nothing after the first call.
		 */
		close(): void;
	}

	/**
	 * View the generated C code for FFI bindings
	 *
	 * You probably won't need this unless there's a bug in the FFI bindings
	 * generator or you're just curious.
	 */
	function viewSource(symbols: Symbols, is_callback?: false): string[];
	function viewSource(callback: FFIFunction, is_callback: true): string;

	/**
	 * Platform-specific file extension name for dynamic libraries
	 *
	 * "." is not included
	 *
	 * @example
	 * ```js
	 * "dylib" // macOS
	 * ```
	 *
	 * @example
	 * ```js
	 * "so" // linux
	 * ```
	 */
	const suffix: string;
}
