UNPKG

netkitty

Version:
357 lines (356 loc) 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseHeader = void 0; const SortPostHandlers_1 = require("../lib/SortPostHandlers"); const FlexibleObject_1 = require("../lib/FlexibleObject"); const ajv_1 = require("ajv"); const CodecSchemaValidateError_1 = require("../../../errors/CodecSchemaValidateError"); const CONSTRUCTOR_VALIDATE_KEY = '__validate'; class BaseHeader { static get CODEC_INSTANCE() { return new this(); } static CREATE_CODEC_INSTANCE_WITCH_CODEC_MODULES(codecData, codecModules) { return this.CREATE_INSTANCE(codecData, codecModules); } static get PROTOCOL_ID() { return this.CODEC_INSTANCE.id; } static get PROTOCOL_NAME() { return this.CODEC_INSTANCE.name; } static get PROTOCOL_SCHEMA() { const schema = JSON.parse(JSON.stringify(this.CODEC_INSTANCE.SCHEMA)); if (!Object.hasOwn(this, CONSTRUCTOR_VALIDATE_KEY)) { const validate = new ajv_1.Ajv({ strict: false, useDefaults: true, coerceTypes: true }).compile(schema); Object.defineProperty(this, CONSTRUCTOR_VALIDATE_KEY, { enumerable: false, configurable: false, value: validate }); } return schema; } static MATCH(codecData, codecModules) { return this.CREATE_CODEC_INSTANCE_WITCH_CODEC_MODULES(codecData, codecModules ? codecModules : []).match(); } static CREATE_INSTANCE(codecData, codecModules) { return new this(codecData, codecModules); } /** * Entire packet buffer data getter */ get packet() { return this.codecData.packet; } /** * Entire packet buffer data setter * @param packet */ set packet(packet) { this.codecData.packet = packet; } /** * The end position of this header in the entire packet's buffer */ get endPos() { return this.startPos + this.headerLength; } /** * Readonly Header length */ get length() { return this.headerLength; } /** * Post packet handlers * @protected */ get postPacketHandlers() { if (this.codecData.postHandlers[this.headerIndex] === undefined) this.codecData.postHandlers[this.headerIndex] = []; return this.codecData.postHandlers[this.headerIndex]; } constructor(codecData, codecModules) { /** * Current header is a protocol or not */ this.isProtocol = true; /** * Encode/Decode error info objects */ this.errors = []; /** * Header schema instance */ this.instance = new FlexibleObject_1.FlexibleObject(); /** * The start position of this header in the entire packet's buffer */ this.startPos = 0; /** * Header length * @protected */ this.headerLength = 0; /** * Current header index in packet headers * @protected */ this.headerIndex = 0; /** * Registered post encode handlers (CodecModule) * @protected */ this.postSelfEncodeHandlers = []; /** * Registered post decode handlers (CodecModule) * @protected */ this.postSelfDecodeHandlers = []; this.codecData = codecData; this.startPos = codecData?.startPos ? codecData.startPos : 0; codecModules = codecModules ? codecModules : []; this.codecModules = codecModules; this.prevCodecModules = [...codecModules]; const prevCodecModuleIndex = this.prevCodecModules.length - 1; this.prevCodecModule = this.prevCodecModules[prevCodecModuleIndex > -1 ? prevCodecModuleIndex : 0]; this.headerIndex = this.prevCodecModules.length; } /** * Get packet data offset * @param offset * @protected */ getPacketOffset(offset) { return this.startPos + offset; } /** * Internal read bytes from buffer * @param offset * @param length * @param expandBufferLength * @param changeHeaderLength * @private */ #readBytes(offset, length, expandBufferLength, changeHeaderLength) { const packetOffset = this.getPacketOffset(offset); let readEndPos = packetOffset + length; if (this.packet.length < readEndPos) { if (expandBufferLength) { this.packet = Buffer.concat([this.packet, Buffer.alloc(readEndPos - this.packet.length, 0)]); } else { readEndPos = this.packet.length; } } const headerLength = readEndPos - this.startPos; if (changeHeaderLength) this.headerLength = this.headerLength < headerLength ? headerLength : this.headerLength; return this.packet.subarray(packetOffset, packetOffset + length); } /** * Read bytes from buffer * @param offset * @param length * @param dryRun * @protected */ readBytes(offset, length, dryRun = false) { return this.#readBytes(offset, length, false, !dryRun); } /** * Write bytes to buffer * @param offset * @param buffer * @protected */ writeBytes(offset, buffer) { const packetOffset = this.getPacketOffset(offset); const writeEndPos = packetOffset + buffer.length; this.#readBytes(offset, buffer.length, true, true); this.packet.fill(buffer, packetOffset, writeEndPos); } /** * Read bits from buffer * @param offset * @param length * @param bitOffset * @param bitLength * @protected */ readBits(offset, length, bitOffset, bitLength) { const buffer = this.#readBytes(offset, length, false, true); const bitString = parseInt(buffer.toString('hex'), 16).toString(2).padStart(length * 8, '0'); return parseInt(bitString.substring(bitOffset, bitOffset + bitLength), 2); } /** * Write bits to buffer * @param offset * @param length * @param bitOffset * @param bitLength * @param value * @protected */ writeBits(offset, length, bitOffset, bitLength, value) { const buffer = this.#readBytes(offset, length, true, true); let bitArray = Array.from(parseInt(buffer.toString('hex'), 16).toString(2).padStart(buffer.length * 8, '0')); const valueBitArray = Array.from(value.toString(2).padStart(bitLength, '0')); bitArray = bitArray.map((bit, index) => { if (index < bitOffset) return bit; if (index >= (bitOffset + bitLength)) return bit; return valueBitArray[index - bitOffset]; }); this.writeBytes(offset, Buffer.from(parseInt(bitArray.join(''), 2).toString(16).padStart(buffer.length * 2, '0'), 'hex')); } /** * Get field codecs from schema tree * @param schema * @param codecName * @param execBeforeSubCodecs * @param codecs * @protected */ getFieldCodecs(schema, codecName, execBeforeSubCodecs, codecs = []) { if (!schema.properties) return codecs; for (const propertyName of Object.keys(schema.properties)) { const fieldSchema = schema.properties[propertyName]; let codec = fieldSchema[codecName]; if (!codec) codec = async () => (void (0)); if (execBeforeSubCodecs) codecs.push(async () => await codec()); if (fieldSchema.properties) this.getFieldCodecs(fieldSchema, codecName, execBeforeSubCodecs, codecs); if (!execBeforeSubCodecs) codecs.push(async () => await codec()); } return codecs; } /** * Record encode/decode error * @param path * @param message * @protected */ recordError(path, message) { this.errors.push({ id: this.id, path: path, message: message }); } /** * Register post encode handler for current codec * @param handler * @param priority * @protected */ addPostSelfEncodeHandler(handler, priority = 0) { this.postSelfEncodeHandlers.push({ priority: priority, handler: handler }); } /** * Register post decode handler for current codec * @param handler * @param priority * @protected */ addPostSelfDecodeHandler(handler, priority = 0) { this.postSelfDecodeHandlers.push({ priority: priority, handler: handler }); } /** * Register post encode handler for packet * @description Registered handler call sequence: LIFO (Last In First Out) * @param handler * @param priority * @protected */ addPostPacketEncodeHandler(handler, priority = 0) { this.postPacketHandlers.push({ priority: priority, handler: handler }); } /** * Register post decode handler for packet * @description Registered handler call sequence: FIFO (First In First Out) * @param handler * @param priority * @protected */ addPostPacketDecodeHandler(handler, priority = 0) { this.postPacketHandlers.push({ priority: priority, handler: handler }); } /** * Validate input json node is valid * @param headerTreeNode */ validate(headerTreeNode) { let validate = this.constructor[CONSTRUCTOR_VALIDATE_KEY]; if (!validate) { validate = new ajv_1.Ajv({ strict: false, useDefaults: true, coerceTypes: true }).compile(this.SCHEMA); this.constructor[CONSTRUCTOR_VALIDATE_KEY] = validate; } const isValid = validate(headerTreeNode); if (!isValid) { let errorObject; if (validate.errors) errorObject = validate.errors[0]; const errorMessage = errorObject?.message ? errorObject.message : 'Unknown Error'; throw new CodecSchemaValidateError_1.CodecSchemaValidateError(errorMessage); } return headerTreeNode; } /** * Decode packet header field by field */ async decode() { const decodes = this.getFieldCodecs(this.SCHEMA, 'decode', true); for (const decode of decodes) { await decode(); } const postSelfDecodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfDecodeHandlers); let postDecodeHandler = postSelfDecodeHandlers.shift(); while (postDecodeHandler) { await postDecodeHandler.handler(); postDecodeHandler = postSelfDecodeHandlers.shift(); } } /** * Encode packet header field by field */ async encode() { const encodes = this.getFieldCodecs(this.SCHEMA, 'encode', false); for (const encode of encodes) { await encode(); } const postSelfEncodeHandlers = (0, SortPostHandlers_1.SortPostHandlers)(this.postSelfEncodeHandlers); let postEncodeHandler = postSelfEncodeHandlers.shift(); while (postEncodeHandler) { await postEncodeHandler.handler(); postEncodeHandler = postSelfEncodeHandlers.shift(); } } } exports.BaseHeader = BaseHeader;