UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

293 lines (226 loc) • 8.35 kB
/** * @readonly * @enum {string} */ import { assert } from "../../assert.js"; import { DataType2DataViewReaders } from "../../binary/data_view/DataType2DataViewReaders.js"; import { DataType2DataViewWriters } from "../../binary/data_view/DataType2DataViewWriters.js"; import { EndianType } from "../../binary/EndianType.js"; import { BinaryDataType } from "../../binary/type/BinaryDataType.js"; import { DataTypeByteSizes } from "../../binary/type/DataTypeByteSizes.js"; import { Cache } from "../../cache/Cache.js"; import { FunctionCompiler } from "../../function/FunctionCompiler.js"; import { objectKeyByValue } from "../../model/object/objectKeyByValue.js"; import { computeStringHash } from "../../primitives/strings/computeStringHash.js"; import { computeHashArray } from "../array/computeHashArray.js"; import { isArrayEqualStrict } from "../array/isArrayEqualStrict.js"; /** * * @param {BinaryDataType[]} types * @param {EndianType} [endianType] * @returns {function(data_view:DataView, byte_offset:number, result:number[]):void} */ function genRowReader(types, endianType = EndianType.BigEndian) { let offset = 0; const lines = []; const numTypes = types.length; for (let i = 0; i < numTypes; i++) { const type = types[i]; const reader_name = DataType2DataViewReaders[type]; assert.isString(reader_name, 'reader_name'); const littleEndianFlag = endianType === EndianType.BigEndian ? 'false' : 'true'; // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView lines.push(`result[${i}] = dataView.${reader_name}(${offset} + byteOffset, ${littleEndianFlag});`); offset += DataTypeByteSizes[type]; } const result = FunctionCompiler.INSTANCE.compile({ args: ['dataView, byteOffset, result'], code: lines.join("\n") }); return result; } /** * * @param {BinaryDataType[]} types * @param {EndianType} [endianType] * @returns {function(data_view:DataView, byte_offset:number, record:number[]):void} */ function genRowWriter(types, endianType = EndianType.BigEndian) { let offset = 0; const lines = []; const numTypes = types.length; for (let i = 0; i < numTypes; i++) { const type = types[i]; const writer_name = DataType2DataViewWriters[type]; assert.isString(writer_name, 'writer_name'); const littleEndianFlag = endianType === EndianType.BigEndian ? 'false' : 'true'; // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView lines.push(`dataView.${writer_name}(${offset} + byteOffset, record[${i}], ${littleEndianFlag});`); offset += DataTypeByteSizes[type]; } const result = FunctionCompiler.INSTANCE.compile({ args: ['dataView, byteOffset, record'], code: lines.join("\n") } ); return result; } /** * * @param {BinaryDataType} type * @param {number} offset * @param {EndianType} [endianType] * @returns {function(data_view:DataView, byte_offset:number, value:number):void} */ function compileDataViewValueWriter(type, offset, endianType = EndianType.BigEndian) { const writeMethod = DataType2DataViewWriters[type]; const littleEndianFlag = endianType === EndianType.BigEndian ? 'false' : 'true'; return FunctionCompiler.INSTANCE.compile({ args: ['dataView, byteOffset, value'], code: `dataView.${writeMethod}(byteOffset+${offset}, value, ${littleEndianFlag});` }); } /** * * @param {BinaryDataType} type * @param {number} offset * @param {EndianType} [endianType] * @returns {function(data_view:DataView, byte_offset:number):number} */ function compileDataViewValueReader(type, offset, endianType = EndianType.BigEndian) { const readMethod = DataType2DataViewReaders[type]; const littleEndianFlag = endianType === EndianType.BigEndian ? 'false' : 'true'; return FunctionCompiler.INSTANCE.compile({ args: ['dataView, byteOffset'], code: `return dataView.${readMethod}(byteOffset+${offset}, ${littleEndianFlag});` }); } /** * This is a schema class for binary data tables of class {@link RowFirstTable} * @class * @copyright Company Named Limited (c) 2025 * @author Alex Goldring */ export class RowFirstTableSpec { /** * * @param {BinaryDataType[]} types * @param {EndianType} [endianness] * @constructor */ constructor( types, endianness = EndianType.BigEndian ) { assert.isArray(types, 'types'); assert.enum(endianness, EndianType, 'endianness'); const numTypes = types.length; /** * @readonly * @type {BinaryDataType[]} */ this.types = types; /** * @readonly * @type {EndianType} */ this.endianType = endianness; /** * @readonly * @type {Uint32Array} */ this.columnOffsets = new Uint32Array(numTypes); let byteOffset = 0; for (let index = 0; index < numTypes; index++) { const type = types[index]; assert.enum(type, BinaryDataType, `type[${index}]`); this.columnOffsets[index] = byteOffset; const columnByteSize = DataTypeByteSizes[type]; byteOffset += columnByteSize; } /** * Number of bytes taken up by a single row of values * @readonly * @type {number} */ this.bytesPerRecord = byteOffset; /** * @readonly * @type {function(DataView, number, number[]): void} */ this.readRowMethod = genRowReader(types, endianness); /** * @readonly * @type {function(DataView, number, number[]): void} */ this.writeRowMethod = genRowWriter(types, endianness); //generate cell readers/writers this.cellWriters = new Array(numTypes); this.cellReaders = new Array(numTypes); for (let i = 0; i < numTypes; i++) { this.cellReaders[i] = compileDataViewValueReader(types[i], this.columnOffsets[i], endianness); this.cellWriters[i] = compileDataViewValueWriter(types[i], this.columnOffsets[i], endianness); } } /** * * @return {number} */ getColumnCount() { return this.types.length; } /** * * @return {number} */ hash() { let hash = this.endianType === EndianType.BigEndian ? 1 : 0; hash += computeHashArray(this.types, computeStringHash); return hash; } /** * * @param {RowFirstTableSpec} other * @returns {boolean} */ equals(other) { if (this.endianType !== other.endianType) { return false; } return isArrayEqualStrict(this.types, other.types); } toString() { return `TableSpec{types=[${this.types.join(', ')}], endian=${objectKeyByValue(EndianType, this.endianType)}}`; } /** * Produces an immutable spec, either creates a new spec or retrieves a cached version * @param {BinaryDataType[]} types * @param {EndianType} [endianType] * @returns {RowFirstTableSpec} */ static get(types, endianType = EndianType.BigEndian) { assert.isArray(types, 'types'); assert.enum(endianType, EndianType, 'endianType'); //compute hash const hash = types.join('.') + ':' + endianType; const cachedValue = SPEC_CACHE.get(hash); if (cachedValue !== null) { return cachedValue; } const newValue = Object.freeze(new RowFirstTableSpec(types)); SPEC_CACHE.put(hash, newValue); return newValue; } } /** * @readonly * @type {boolean} */ RowFirstTableSpec.prototype.isRowFirstTableSpec = true; /** * @readonly * @type {Cache<string,RowFirstTableSpec>} */ const SPEC_CACHE = new Cache({ keyHashFunction: computeStringHash });