import Dispatcher from "./Dispatcher"; import * as CRC from './CRC'; import { bitsToBytes, bitsToInt, numberToBytes } from "./converters"; const dispatcher = new Dispatcher('StreamManager', ['change']); const BITS = []; let BITS_PER_PACKET = 0; let SEGMENTS_PER_PACKET = 0; let BITS_PER_SEGMENT = 0; let STREAM_HEADERS = []; let SEGMENT_ENCODING = { encode: bits => bits, decode: bits => bits }; let PACKET_ENCODING = { encode: bits => bits, decode: bits => bits } export const addEventListener = dispatcher.addListener; export const removeEventListener = dispatcher.removeListener; export const reset = () => { if(BITS.length !== 0) { BITS.length = 0; dispatcher.emit('change'); } } export const changeConfiguration = ({ segmentsPerPacket, bitsPerPacket, bitsPerSegment, streamHeaders }) => { BITS_PER_PACKET = bitsPerPacket; SEGMENTS_PER_PACKET = segmentsPerPacket; BITS_PER_SEGMENT = bitsPerSegment; STREAM_HEADERS = streamHeaders; } const noEncoding = bits => bits; export const setSegmentEncoding = ({ encode, decode } = {}) => { SEGMENT_ENCODING.encode = encode ?? noEncoding; SEGMENT_ENCODING.decode = decode ?? noEncoding; } export const setPacketEncoding = ({ encode, decode } = {}) => { PACKET_ENCODING.encode = encode ?? noEncoding; PACKET_ENCODING.decode = decode ?? noEncoding; } export const addBits = ( packetIndex, segmentIndex, bits ) => { if(BITS[packetIndex] === undefined) { BITS[packetIndex] = []; } const oldBits = BITS[packetIndex][segmentIndex]; BITS[packetIndex][segmentIndex] = bits; if(hasNewBits(oldBits, bits)) dispatcher.emit('change'); } const sumSegmentBits = (sum, segment) => sum + segment.length; const sumPacketBits = (sum, packet) => sum + packet.reduce(sumSegmentBits, 0); export const sumTotalBits = () => BITS.reduce(sumPacketBits, 0); const hasNewBits = (oldBits = [], bits = []) => { if(oldBits.length === 0 && bits.length === BITS_PER_SEGMENT) return true; for(let i = 0; i < BITS_PER_SEGMENT; i++) { let a = oldBits[i] ?? 0; let b = bits[i] ?? 0; if(a !== b) return true; } return false; } export const getPacketReceivedCount = () => { if(BITS.length === 0) return 1; return BITS.length; } export const getStreamBits = () => { const bits = []; const packetCount = getPacketReceivedCount(); for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) { const packet = BITS[packetIndex] ?? []; for(let segmentIndex = 0; segmentIndex < SEGMENTS_PER_PACKET; segmentIndex++) { let segment = packet[segmentIndex] ?? []; for(let bitIndex = 0; bitIndex < BITS_PER_SEGMENT; bitIndex++) { const bit = segment[bitIndex]; bits.push(bit ?? 0); } } } return bits; } export const getPacketSegmentBits = (packetIndex, segmentIndex) => BITS[packetIndex]?.[segmentIndex]; export const getAllPacketBits = () => { const packetCount = getPacketReceivedCount(); const bits = []; for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) { bits.push(...getPacketBits(packetIndex)); } return bits; } export const getAllPacketBitsDecoded = () => { const packetCount = getPacketReceivedCount(); const bits = []; for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) { bits.push(...getPacketBitsDecoded(packetIndex)); } return bits; } export const getDataBytes = () => { const bits = getAllPacketBitsDecoded(); bits.splice(0, getStreamHeaderBitCount()); const bytes = bitsToBytes(bits); if(isTransferByteCountTrusted()) { bytes.length = getTransferByteCount(); } return bytes; } export const getPacketBits = (packetIndex, defaultBit = 0) => { const bits = []; const packet = BITS[packetIndex] ?? []; for(let segmentIndex = 0; segmentIndex < SEGMENTS_PER_PACKET; segmentIndex++) { let segment = packet[segmentIndex] ?? []; segment = SEGMENT_ENCODING.decode(segment); for(let bitIndex = 0; bitIndex < BITS_PER_SEGMENT; bitIndex++) { const bit = segment[bitIndex]; bits.push(bit ?? defaultBit); if(bits.length === BITS_PER_PACKET) return bits; } } while(bits.length < BITS_PER_PACKET) bits.push(defaultBit); return bits; } export const getPacketBitsDecoded = (packetIndex, defaultBit = 0) => { const bits = getPacketBits(packetIndex, defaultBit); return PACKET_ENCODING.decode(bits); } const getStreamHeaderBitCount = () => { return Object.keys(STREAM_HEADERS).reduce((lastBit, key) => { const {index = 0, length = 0} = STREAM_HEADERS[key]; if(length === 0) return lastBit; if(lastBit < index + length) return index + length; return lastBit; }, 0); } const getStreamHeaderBits = name => { const header = STREAM_HEADERS[name]; if(!header) return []; const { index, length } = header; if(length === 0) return []; const packetCount = getPacketReceivedCount(); const bits = []; for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) { bits.push(...getPacketBitsDecoded(packetIndex, 1)); if(bits.length >= index + length) break; } return bits.slice(index, index + length); } export const getTransferByteCount = () => { const name = 'transfer byte count'; const length = STREAM_HEADERS[name].length; if(length === 0) return 1; const bits = getStreamHeaderBits(name); return bitsToInt(bits, length); } export const getTransferByteCountCrc = () => { const name = 'transfer byte count crc'; const length = STREAM_HEADERS[name].length; if(length === 0) return 0; const bits = getStreamHeaderBits(name); if(bits.length !== length) return CRC.INVALID; return bitsToInt(bits, length); } export const getTransferByteCountActualCrc = () => { const countBits = getStreamHeaderBits('transfer byte count').length; if(countBits === 0) return 0; const crcBits = getStreamHeaderBits('transfer byte count crc').length; if(crcBits === 0) return 0; const count = getTransferByteCount(); const bytesOfCount = numberToBytes(count, countBits); return CRC.check(bytesOfCount, crcBits) } export const isTransferByteCountTrusted = () => { return getTransferByteCountCrc() === getTransferByteCountActualCrc(); } export function getTransferDataCrc() { const name = 'transfer byte crc'; const length = STREAM_HEADERS[name].length; if(length === 0) return 0; const bits = getStreamHeaderBits(name); if(bits.length !== length) return CRC.INVALID; return bitsToInt(bits, length); } export function getTransferActualDataCrc() { const name = 'transfer byte crc'; const length = STREAM_HEADERS[name].length; if(length === 0) return 0; const crcBits = getStreamHeaderBits(name).length; const bytes = getDataBytes(); return CRC.check(bytes, crcBits); } export const isTransferDataTrusted = () => { return getTransferDataCrc() === getTransferActualDataCrc(); }