@eventmsg/core
Version:
EventMsgV3 TypeScript library - Core protocol implementation with transport abstraction
132 lines (131 loc) • 3.59 kB
JavaScript
//#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