/** * @module * * Array types and factory. */ import { MeekValueMap } from '@hqtsm/meek/valuemap'; import { type Class, constant, toStringTag } from '@hqtsm/class'; import type { MemberInfos } from './members.js'; import type { ArrayBufferType } from './native.js'; import { pointer, type Ptr, type PtrConstructor } from './ptr.js'; import type { Type, TypeConstructor } from './type.js'; /** * Array. * * @template T Value type. * @template TArrayBuffer Buffer type. */ export interface Arr< T = never, TArrayBuffer extends ArrayBufferType = ArrayBufferType, > extends Ptr, Type { /** * Array length. */ readonly length: number; /** * Get iterator for values. * * @returns Value iterator. */ [Symbol.iterator](): ArrayIterator; /** * Get iterator for entries. * * @returns Entry iterator. */ entries(): ArrayIterator<[number, T]>; /** * Get iterator for keys. * * @returns Key iterator. */ keys(): ArrayIterator; /** * Get iterator for values. * * @returns Value iterator. */ values(): ArrayIterator; /** * Get value at index. * * @param i Index, negative to index from end. * @returns Value at index. */ at(i: number): T | undefined; } /** * Array constructor. * * @template T Array type. * @template TArrayBuffer Buffer type. */ export interface ArrConstructor< T extends Arr = Arr, TArrayBuffer extends ArrayBufferType = ArrayBufferType, > extends Omit, never>, Omit, never> { /** * Create instance for buffer. * * @template _TArrayBuffer Buffer type. * @param buffer Buffer data. * @param byteOffset Byte offset into buffer. * @param littleEndian Host endian, little endian, big endian. */ new <_TArrayBuffer extends TArrayBuffer>( buffer: _TArrayBuffer, byteOffset?: number, littleEndian?: boolean | null, ): T & Arr; /** * Create instance for buffer. * * @param buffer Buffer data. * @param byteOffset Byte offset into buffer. * @param littleEndian Host endian, little endian, big endian. */ new ( buffer: TArrayBuffer, byteOffset?: number, littleEndian?: boolean | null, ): T & Arr; /** * Array prototype. */ readonly prototype: T & Arr; /** * Array length. */ readonly LENGTH: number; } /** * Array class. * * @template T Array type. */ export type ArrClass> = Class>; const arrays = new WeakMap< PtrConstructor>, // deno-lint-ignore no-explicit-any MeekValueMap>> >(); /** * Create array of length from type/array. * * @template T Value type. * @param Type Type constructor. * @param length Array length. * @returns Array constructor. */ export function array( Type: TypeConstructor, length: number, ): ArrConstructor>; /** * Create array of length from pointer. * * @template T Pointer type. * @param Ptr Pointer constructor. * @param length Array length. * @returns Array constructor. */ export function array( Ptr: PtrConstructor> & { BYTE_LENGTH?: never }, length: number, ): ArrConstructor>; /** * Create array of length from type/array or pointer. * * @template T Value type. * @param Type Type constructor or pointer constructor. * @param length Array length. * @returns Array constructor. */ export function array( TypePtr: TypeConstructor | PtrConstructor>, length: number, ): ArrConstructor> { length = (+length || 0) - (length % 1 || 0); if (length < 0 || !(length < Infinity)) { throw new RangeError(`Invalid length: ${length}`); } const Ptr = 'BYTE_LENGTH' in TypePtr ? pointer(TypePtr) : TypePtr; let lengths = arrays.get(Ptr); if (!lengths) { arrays.set(Ptr, lengths = new MeekValueMap()); } let r = lengths.get(length); if (!r) { const name = `${Ptr.name}[${length}]`; const tag = `${Ptr.prototype[Symbol.toStringTag]}[${length}]`; const members = new WeakMap>, MemberInfos>(); lengths.set( length, r = { [name]: class extends Ptr implements Arr { public get byteLength(): number { return (this.constructor as ArrClass>) .BYTE_LENGTH; } public get length(): number { return length; } public *[Symbol.iterator](): ArrayIterator { for (let i = 0; i < length; i++) { yield this[i]; } } public *entries(): ArrayIterator<[number, T]> { for (let i = 0; i < length; i++) { yield [i, this[i]]; } } public *keys(): ArrayIterator { for (let i = 0; i < length; i++) { yield i; } } public *values(): ArrayIterator { for (let i = 0; i < length; i++) { yield this[i]; } } public at(i: number): T | undefined { i = (+i || 0) - (i % 1 || 0); if ((i < 0 ? i += length : i) >= 0 && i < length) { return this[i]; } } public static readonly BYTE_LENGTH = ( Ptr.BYTES_PER_ELEMENT * length ); public static readonly LENGTH = length; public static override get MEMBERS(): MemberInfos { let r = members.get(this); if (!r) { members.set( this, r = Object.create( Object.getPrototypeOf(this).MEMBERS, ) as MemberInfos, ); } return r; } static { toStringTag(this, tag); constant(this, 'BYTE_LENGTH'); constant(this, 'LENGTH'); } }, }[name], ); } return r; }