import { BinaryInfo } from './BinaryInfo' import { FLAG_BYTE, FLAG_EVENT, FLAG_EXTENDED, FLAG_FIELD, FLAG_GLOBAL, WEB_EVENTS, WEB_GLOBALS } from './constants' const getHeaderBitLength = (key: number) => (key < 256 ? 2 : 3) type Value = number | null | string export const encodeWAM = (binaryInfo: BinaryInfo) => { binaryInfo.buffer = [] encodeWAMHeader(binaryInfo) encodeEvents(binaryInfo) console.log(binaryInfo.buffer) const totalSize = binaryInfo.buffer .map((a) => a.length) .reduce((a, b) => a + b) const buffer = Buffer.alloc(totalSize) let offset = 0 binaryInfo.buffer.forEach((buffer_) => { buffer_.copy(buffer, offset) offset += buffer_.length }) return buffer } function encodeWAMHeader(binaryInfo: BinaryInfo) { const headerBuffer = Buffer.alloc(8) // starting buffer headerBuffer.write('WAM', 0, 'utf8') headerBuffer.writeUInt8(binaryInfo.protocolVersion, 3) headerBuffer.writeUInt8(1, 4) //random flag headerBuffer.writeUInt16BE(binaryInfo.sequence, 5) headerBuffer.writeUInt8(0, 7) // regular channel binaryInfo.buffer.push(headerBuffer) } function encodeGlobalAttributes(binaryInfo: BinaryInfo, globals: {[key: string]: Value}) { for(const [key, _value] of Object.entries(globals)) { const id = WEB_GLOBALS.find(a => a?.name === key)!.id let value = _value if(typeof value === 'boolean') { value = value ? 1 : 0 } binaryInfo.buffer.push(serializeData(id, value, FLAG_GLOBAL)) } } function encodeEvents(binaryInfo: BinaryInfo) { for(const [ name, { props, globals }, ] of binaryInfo.events.map((a) => Object.entries(a)[0])) { encodeGlobalAttributes(binaryInfo, globals) const event = WEB_EVENTS.find((a) => a.name === name)! const props_ = Object.entries(props) let extended = false for(const [, value] of props_) { extended ||= value !== null } const eventFlag = extended ? FLAG_EVENT : FLAG_EVENT | FLAG_EXTENDED binaryInfo.buffer.push(serializeData(event.id, -event.weight, eventFlag)) for(let i = 0; i < props_.length; i++) { const [key, _value] = props_[i] const id = (event.props)[key][0] extended = i < (props_.length - 1) let value = _value if(typeof value === 'boolean') { value = value ? 1 : 0 } const fieldFlag = extended ? FLAG_EVENT : FLAG_FIELD | FLAG_EXTENDED binaryInfo.buffer.push(serializeData(id, value as Value, fieldFlag)) } } } function serializeData(key: number, value: Value, flag: number): Buffer { const bufferLength = getHeaderBitLength(key) let buffer: Buffer let offset = 0 if(value === null) { if(flag === FLAG_GLOBAL) { buffer = Buffer.alloc(bufferLength) offset = serializeHeader(buffer, offset, key, flag) return buffer } } else if(typeof value === 'number' && Number.isInteger(value)) { // is number if(value === 0 || value === 1) { buffer = Buffer.alloc(bufferLength) offset = serializeHeader(buffer, offset, key, flag | ((value + 1) << 4)) return buffer } else if(-128 <= value && value < 128) { buffer = Buffer.alloc(bufferLength + 1) offset = serializeHeader(buffer, offset, key, flag | (3 << 4)) buffer.writeInt8(value, offset) return buffer } else if(-32768 <= value && value < 32768) { buffer = Buffer.alloc(bufferLength + 2) offset = serializeHeader(buffer, offset, key, flag | (4 << 4)) buffer.writeInt16LE(value, offset) return buffer } else if(-2147483648 <= value && value < 2147483648) { buffer = Buffer.alloc(bufferLength + 4) offset = serializeHeader(buffer, offset, key, flag | (5 << 4)) buffer.writeInt32LE(value, offset) return buffer } else { buffer = Buffer.alloc(bufferLength + 8) offset = serializeHeader(buffer, offset, key, flag | (7 << 4)) buffer.writeDoubleLE(value, offset) return buffer } } else if(typeof value === 'number') { // is float buffer = Buffer.alloc(bufferLength + 8) offset = serializeHeader(buffer, offset, key, flag | (7 << 4)) buffer.writeDoubleLE(value, offset) return buffer } else if(typeof value === 'string') { // is string const utf8Bytes = Buffer.byteLength(value, 'utf8') if(utf8Bytes < 256) { buffer = Buffer.alloc(bufferLength + 1 + utf8Bytes) offset = serializeHeader(buffer, offset, key, flag | (8 << 4)) buffer.writeUint8(utf8Bytes, offset++) } else if(utf8Bytes < 65536) { buffer = Buffer.alloc(bufferLength + 2 + utf8Bytes) offset = serializeHeader(buffer, offset, key, flag | (9 << 4)) buffer.writeUInt16LE(utf8Bytes, offset) offset += 2 } else { buffer = Buffer.alloc(bufferLength + 4 + utf8Bytes) offset = serializeHeader(buffer, offset, key, flag | (10 << 4)) buffer.writeUInt32LE(utf8Bytes, offset) offset += 4 } buffer.write(value, offset, 'utf8') return buffer } throw 'missing' } function serializeHeader( buffer: Buffer, offset: number, key: number, flag: number ) { if(key < 256) { buffer.writeUInt8(flag, offset) offset += 1 buffer.writeUInt8(key, offset) offset += 1 } else { buffer.writeUInt8(flag | FLAG_BYTE, offset) offset += 1 buffer.writeUInt16LE(key, offset) offset += 2 } return offset }