Source: types/PointerType.js

import { MemoryManager } from '../MemoryManager'
import { Pointer } from '../Pointer'

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

/**
 * A pointer type
 * @template T
 * @extends {ReferenceType<Pointer<T>>}
 */
export class PointerType extends ReferenceType {
  /**
   * Construct a pointer type.
   * @param {Type<T>} type The type pointed to
   */
  constructor (type) {
    super()
    this.type = type
  }

  /**
   * Free an allocated pointer
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} address The address of the pointer to be freed
   * @param {number} unmarshalledIndex The index of the unmarshalled value or -1
   * @param {Array<*>} unmarshalledArgs The unmarshalled arguments
   * @returns {void}
   */
  free (memoryManager, address, unmarshalledIndex, unmarshalledArgs) {
    try {
      const marshalledAddress = memoryManager.dataView.getUint32(address)
      this.type.free(memoryManager, marshalledAddress, unmarshalledIndex, unmarshalledArgs)
    } finally {
      memoryManager.free(address)
    }
  }

  /**
   * Allocate memory for a pointer
   * @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) {
    const address = memoryManager.malloc(Uint32Array.BYTES_PER_ELEMENT)
    return address
  }

  /**
   * Marshal a pointer
   * @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 pointer in memory
   */
  marshall (memoryManager, unmarshalledIndex, unmarshalledArgs) {
    const address = this.alloc(memoryManager, unmarshalledIndex, unmarshalledArgs)
    const unmarshalledValue = /** @type {Pointer<T>} */ (unmarshalledArgs[unmarshalledIndex])
    const marshalledAddress = /** @type {number} */ (this.type.marshall(memoryManager, 0, [unmarshalledValue.contents]))
    memoryManager.dataView.setUint32(address, marshalledAddress)
    return address
  }

  /**
   * Unmarshall a pointer.
   * @param {MemoryManager} memoryManager The memory manager
   * @param {number} address The address of the pointer in memory
   * @param {number} unmarshalledIndex The index to the unmarshalled value of -1
   * @param {Array<*>} unmarshalledArgs the unmarshalled arguments
   * @returns {Pointer<T>} The unmarshalled pointer
   */
  unmarshall (memoryManager, address, unmarshalledIndex, unmarshalledArgs) {
    try {
      const marshalledAddress = memoryManager.dataView.getUint32(address)
      return new Pointer(this.type.unmarshall(memoryManager, marshalledAddress, -1, []))
    } finally {
      memoryManager.free(address)
    }
  }

  /**
   * Copy a pointer
   * @param {Pointer<T>} dest The destination pointer
   * @param {Pointer<T>} source The source pointer
   * @returns {Pointer<T>} The destination pointer
   */
  copy (dest, source) {
    dest.contents = source.contents
    return dest
  }

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