cyberchef/src/core/lib/COBS.mjs

87 lines
2.1 KiB
JavaScript

/**
* @author Imantas Lukenskas [imantas@lukenskas.dev]
* @copyright Imantas Lukenskas 2026
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/**
* COBS-encode a byte array
* @param {Uint8Array} data
* @return {Uint8Array}
*/
export function toCobs(data) {
if (!data || data.length === 0) {
return new Uint8Array();
}
const output = [];
data = [0, ...data];
while (data.length > 0) {
const endIndex = data.findIndex((value, index) => value === 0 && index > 0);
if ((endIndex < 0 || endIndex > 254) && data.length > 254) {
output.push(255);
output.push(...data.slice(1, 255));
data = data.slice(255);
if (data.length !== 0) {
data = [0, ...data];
}
} else if (endIndex < 0) {
output.push(data.length);
output.push(...data.slice(1));
data = [];
} else {
output.push(endIndex);
output.push(...data.slice(1, endIndex));
data = data.slice(endIndex);
}
}
return output;
}
/**
* COBS-decode a byte array
* @param {Uint8Array} data
* @return {Uint8Array}
*/
export function fromCobs(data) {
if (!data || data.length === 0) {
return new Uint8Array();
}
if (data.findIndex((value) => value === 0) >= 0) {
throw new OperationError("Could not decode from COBS: payload must not contain a 0x00 byte");
}
const output = [];
while (data.length > 0) {
if (data[0] === 0xFF) {
output.push(...data.slice(1, 255));
data = data.slice(255);
} else {
const nextZeroIndex = data[0];
output.push(...data.slice(1, nextZeroIndex));
data = data.slice(nextZeroIndex);
let blockSize = data[0];
while (data.length > 0) {
output.push(0, ...data.slice(1, blockSize));
data = data.slice(blockSize);
if (blockSize === 0xFF) {
break;
}
blockSize = data[0];
}
}
}
return output;
}