Source: types/TypedArrayType.js

import { MemoryManager } from '../MemoryManager'

import { ReferenceType } from './ReferenceType'
import { Type } from './Type'

 /**
 * TypedArray
 * @typedef {Int8Array|Int16Array|Int32Array|BigInt64Array|Uint8Array|Uint16Array|Uint32Array|BigUint64Array|Float32Array|Float64Array} TypedArray
 */

 /**
 * Gets the length
 * @callback lengthCallback
 * @param {number} unmarshalledIndex The index of the unmarshalled value or -1
 * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
 * @returns {number} The length of the array
 */

/**
 * An array type
 * @template T
 * @extends {ReferenceType<TypedArray>}
 */
export class TypedArrayType extends ReferenceType {
  /**
   * Construct an array type
   * @param {Type<T>} type The type of the elements in the array
   * @param {number|lengthCallback} [length] The optional length of the array
   */
  constructor (type, length = null) {
    super()
    this.type = type
    this.length = length
  }

  /**
   * Get the length of the array
   * @param {number} unmarshalledIndex The index of the unmarshalled argument or -1
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
   * @returns {number} The length of the array.
   */
  getLength (unmarshalledIndex, unmarshalledArgs) {
    if (typeof this.length === 'number') {
      return this.length
    } else if (typeof this.length === 'function') {
      return this.length(unmarshalledIndex, unmarshalledArgs)
    } else if (unmarshalledIndex !== -1) {
      const array = /** @type {TypedArray} */ (unmarshalledArgs[unmarshalledIndex])
      return array.length
    } else {
      throw RangeError('Unable to establish the length of the array')
    }
  }

  /**
   * Allocate memory for the array.
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} unmarshalledIndex The index of the unmarshalled value or -1
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
   * @returns {number} The address of the allocated memory.
   */
  alloc (memoryManager, unmarshalledIndex, unmarshalledArgs) {
    if (unmarshalledIndex !== -1) {
      return unmarshalledArgs[unmarshalledIndex].byteOffset
    } else {
      const length = this.getLength(unmarshalledIndex, unmarshalledArgs)
      return memoryManager.malloc(length * this.type.TypedArrayType.BYTES_PER_ELEMENT)
    }
  }

  /**
   * Free allocated memory.
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} address The address of the memory to be freed
   * @param {number} unmarshalledIndex The index to the unmarshalled array or -1
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
   * @returns {void}
   */
  free (memoryManager, address, unmarshalledIndex, unmarshalledArgs) {
    // The finalizer handles freeing.
  }

  /**
   * Marshall a typed array.
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} unmarshalledIndex The index of the value to be marshalled
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
   * @returns {number} The address of the marshalled array
   */
  marshall (memoryManager, unmarshalledIndex, unmarshalledArgs) {
    // Simply return the address.
    const unmarshalledValue = unmarshalledArgs[unmarshalledIndex]
    return unmarshalledValue.byteOffset
  }

  /**
   * Unmarshall a typed array.
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} address The address of the marshalled array
   * @param {number} unmarshalledIndex The index of the unmarshalled value or -1
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments.
   * @returns {TypedArray} The unmarshalled array
   */
  unmarshall (memoryManager, address, unmarshalledIndex, unmarshalledArgs) {
    if (unmarshalledIndex !== -1) {
      // We assume typed arrays are references to memory in the WebAssembly, so
      // the unmarshalled value and the marshalled values are the same,
      return /** @type {TypedArray} */ unmarshalledArgs[unmarshalledIndex]
    } else {
      // Create the typed array. Note this is just a view into the WebAssembly
      // memory buffer.
      const length = this.getLength(unmarshalledIndex, unmarshalledArgs)
      const typedArray = new this.type.TypedArrayType(
        memoryManager.memory.buffer,
        address,
        length)
      memoryManager.freeWhenFinalized(typedArray, address)
      return typedArray
    }
  }

  /**
   * Copy an array
   * @param {TypedArray} dest The array to receive the data
   * @param {TypedArray} source The source array
   * @returns {TypedArray} The array to which the data was copied.
   */
  copy (dest, source) {
    // Nothing to do.
    return dest
  }

  get mangledName() {
    return `t(${this.type.mangledName})`
  }
}