Source: accessors/record-accessor.js

/**
 * @author Toru Nagashima
 * @copyright 2016 Toru Nagashima. All rights reserved.
 * See LICENSE file in root directory for full license.
 */
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const assert = require("../assert")
const BinaryRecord = require("../binary-record").BinaryRecord
const Accessor = require("./accessor").Accessor

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const instances = new Map()
const uid = (() => {
    let n = 0
    return () => {
        const n1 = (n = ((n + 1) & 0xFFFF)).toString(16)
        const n2 = Date.now().toString(16)
        return `_${n2}_${n1}`
    }
})()

/**
 * The field accessor for sub record fields.
 *
 * @private
 */
class RecordAccessor extends Accessor {
    /**
     * @param {class} Record - The class of the record to access.
     */
    constructor(Record) {
        assert.function(Record, "Record")
        assert(
            Record.prototype instanceof BinaryRecord,
            "'Record' should be a sub type of BinaryRecord, " +
            `but got ${Record.name}.`
        )
        super(
            Record.name,
            Record.bitLength,
            {hasInitCode: true, readOnly: true}
        )
        this.uid = uid()
        this.Record = Record
    }

    /**
     * Gets the initialize logic for this accessor.
     *
     * @param {string} name - The property name to initialize.
     * @param {string} bitOffset - The data position to initialize.
     * @returns {string} The initialize code of the property.
     */
    initCode(name, bitOffset) {
        assert.numberOrString(name, "name")
        assert.bitOffsetX8(bitOffset, this.name)

        const byteOffset = bitOffset >> 3

        return (
            `this[s.subRecords]._${name} = ` +
            `RecordPool.${this.uid}.view(this[s.buffer], ${byteOffset})`
        )
    }

    /**
     * Gets the getter/setter pair logic for this accessor.
     *
     * @param {string} name - The property name to access.
     * @param {string} _bitOffset - The data position to access.
     * @returns {string} The getter/setter code of the property.
     */
    propertyCode(name, _bitOffset) {
        assert.numberOrString(name, "name")

        return `get ${name}() {
        return this[s.subRecords]._${name}
    }`
    }
}

//------------------------------------------------------------------------------
// Exports
//------------------------------------------------------------------------------

exports.record = (Record) => {
    let accessor = instances.get(Record)

    if (accessor == null) {
        accessor = new RecordAccessor(Record)
        instances.set(Record, accessor)
    }

    return accessor
}