UNPKG

@eventmsg/core

Version:

EventMsgV3 TypeScript library - Core protocol implementation with transport abstraction

132 lines (131 loc) 3.59 kB
//#region src/internal/byte-stuffing.ts /** * EventMsgV3 Byte Stuffing Implementation * * Implements the exact byte stuffing algorithm from the C implementation * to ensure protocol compatibility. */ /** * Control characters used in EventMsgV3 protocol */ const CONTROL_CHARS = { SOH: 1, STX: 2, EOT: 4, US: 31, ESC: 27 }; /** * XOR mask for byte stuffing */ const STUFF_MASK = 32; /** * Check if a byte is a control character that needs stuffing */ function isControlChar(byte) { return byte === CONTROL_CHARS.SOH || byte === CONTROL_CHARS.STX || byte === CONTROL_CHARS.EOT || byte === CONTROL_CHARS.US || byte === CONTROL_CHARS.ESC; } /** * Stuff (encode) bytes by escaping control characters * * Algorithm: * - If byte is a control character: Insert ESC, then XOR byte with 0x20 * - Otherwise: Insert byte as-is * * @param data Input data to stuff * @returns Stuffed data */ function stuff(data) { const result = []; for (let i = 0; i < data.length; i++) { const byte = data[i]; if (byte === void 0) continue; if (isControlChar(byte)) { result.push(CONTROL_CHARS.ESC); result.push(byte ^ STUFF_MASK); } else result.push(byte); } return new Uint8Array(result); } /** * Unstuff (decode) bytes by handling escaped characters * * Algorithm: * - If byte is ESC: Mark next byte as escaped, continue * - If previous byte was ESC: XOR current byte with 0x20, add to output * - Otherwise: Add byte as-is to output * * @param data Stuffed data to unstuff * @returns Unstuffed data * @throws {Error} If data contains invalid escape sequences */ function unstuff(data) { const result = []; let escaped = false; for (let i = 0; i < data.length; i++) { const byte = data[i]; if (byte === void 0) continue; if (escaped) { result.push(byte ^ STUFF_MASK); escaped = false; } else if (byte === CONTROL_CHARS.ESC) escaped = true; else result.push(byte); } if (escaped) throw new Error("Invalid byte stuffing: data ends with incomplete escape sequence"); return new Uint8Array(result); } /** * Calculate the maximum possible size after stuffing * In worst case, every byte could be a control character, doubling the size * * @param originalSize Original data size * @returns Maximum size after stuffing */ function getMaxStuffedSize(originalSize) { return originalSize * 2; } /** * Calculate the minimum possible size after unstuffing * In best case, no bytes are stuffed * * @param stuffedSize Stuffed data size * @returns Minimum size after unstuffing */ function getMinUnstuffedSize(stuffedSize) { return Math.floor(stuffedSize / 2); } /** * Test if data contains any control characters that would need stuffing * * @param data Data to test * @returns True if data contains control characters */ function needsStuffing(data) { for (let i = 0; i < data.length; i++) { const byte = data[i]; if (byte !== void 0 && isControlChar(byte)) return true; } return false; } /** * Validate that stuffed data has proper escape sequences * * @param data Stuffed data to validate * @returns True if data has valid stuffing */ function isValidStuffing(data) { let escaped = false; for (let i = 0; i < data.length; i++) { const byte = data[i]; if (byte === void 0) continue; if (escaped) { if (!isControlChar(byte ^ STUFF_MASK)) return false; escaped = false; } else if (byte === CONTROL_CHARS.ESC) escaped = true; else if (isControlChar(byte)) return false; } return !escaped; } //#endregion export { CONTROL_CHARS, getMaxStuffedSize, getMinUnstuffedSize, isValidStuffing, needsStuffing, stuff, unstuff }; //# sourceMappingURL=byte-stuffing.js.map