Skip to content

Commit 1d2b7a5

Browse files
committed
Add COBS encoding/decoding operations
1 parent cc2c6d2 commit 1d2b7a5

File tree

6 files changed

+591
-1
lines changed

6 files changed

+591
-1
lines changed

src/core/config/Categories.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@
8181
"Rison Decode",
8282
"To Modhex",
8383
"From Modhex",
84-
"MIME Decoding"
84+
"MIME Decoding",
85+
"To COBS",
86+
"From COBS"
8587
]
8688
},
8789
{

src/core/lib/COBS.mjs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @author Imantas Lukenskas [imantas@lukenskas.dev]
3+
* @copyright Imantas Lukenskas 2026
4+
* @license Apache-2.0
5+
*/
6+
7+
/**
8+
* COBS-encode a byte array
9+
* @param {Uint8Array} data
10+
* @return {Uint8Array}
11+
*/
12+
export function toCobs(data) {
13+
if (!data || data.length === 0) {
14+
return new Uint8Array();
15+
}
16+
17+
const output = [];
18+
data = [0, ...data];
19+
20+
while (data.length > 0) {
21+
const endIndex = data.findIndex((value, index) => value === 0 && index > 0);
22+
23+
if ((endIndex < 0 || endIndex > 254) && data.length > 254) {
24+
output.push(255);
25+
output.push(...data.slice(1, 255));
26+
data = [...data.slice(255)];
27+
if (data.length !== 0) {
28+
data = [0, ...data];
29+
}
30+
} else if (endIndex < 0) {
31+
output.push(data.length);
32+
output.push(...data.slice(1));
33+
data = [];
34+
} else {
35+
output.push(endIndex);
36+
output.push(...data.slice(1, endIndex));
37+
data = data.slice(endIndex);
38+
}
39+
}
40+
41+
return output;
42+
}
43+
44+
/**
45+
* COBS-decode a byte array
46+
* @param {Uint8Array} data
47+
* @return {Uint8Array}
48+
*/
49+
export function fromCobs(data) {
50+
if (!data || data.length === 0) {
51+
return new Uint8Array();
52+
}
53+
54+
const output = [];
55+
56+
while (data.length > 0) {
57+
if (data[0] === 0xFF) {
58+
output.push(...data.slice(1, 255));
59+
data = data.slice(255);
60+
} else {
61+
const nextZeroIndex = data[0];
62+
output.push(...data.slice(1, nextZeroIndex))
63+
data = data.slice(nextZeroIndex);
64+
65+
let blockSize = data[0];
66+
while (data.length > 0) {
67+
output.push(0, ...data.slice(1, blockSize));
68+
data = data.slice(blockSize);
69+
70+
if (blockSize === 0xFF) {
71+
break;
72+
}
73+
74+
blockSize = data[0];
75+
}
76+
}
77+
}
78+
79+
return output;
80+
}

src/core/operations/FromCOBS.mjs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @author Imantas Lukenskas [imantas@lukenskas.dev]
3+
* @copyright Imantas Lukenskas 2026
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import {fromCobs} from "../lib/COBS.mjs";
9+
10+
/**
11+
* From COBS operation
12+
*/
13+
class FromCOBS extends Operation {
14+
/**
15+
* FromCOBS constructor
16+
*/
17+
constructor() {
18+
super();
19+
20+
this.name = "From COBS";
21+
this.module = "Default";
22+
this.description = "Decodes COBS encoded bytes";
23+
this.infoURL = "https://wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing";
24+
this.inputType = "byteArray";
25+
this.outputType = "byteArray";
26+
}
27+
28+
/**
29+
* @param {byteArray} input
30+
* @param {Object[]} args
31+
* @returns {byteArray}
32+
*/
33+
run(input, args) {
34+
return fromCobs(input);
35+
}
36+
}
37+
38+
export default FromCOBS;

src/core/operations/ToCOBS.mjs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @author Imantas Lukenskas [imantas@lukenskas.dev]
3+
* @copyright Imantas Lukenskas 2026
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import {toCobs} from "../lib/COBS.mjs";
9+
10+
/**
11+
* To COBS operation
12+
*/
13+
class ToCOBS extends Operation {
14+
/**
15+
* ToCOBS constructor
16+
*/
17+
constructor() {
18+
super();
19+
20+
this.name = "To COBS";
21+
this.module = "Default";
22+
this.description = "Encodes bytes in COBS format";
23+
this.infoURL = "https://wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing";
24+
this.inputType = "byteArray";
25+
this.outputType = "byteArray";
26+
}
27+
28+
/**
29+
* @param {byteArray} input
30+
* @param {Object[]} args
31+
* @returns {byteArray}
32+
*/
33+
run(input, args) {
34+
return toCobs(input);
35+
}
36+
}
37+
38+
export default ToCOBS;

tests/operations/index.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ import "./tests/JSONtoYAML.mjs";
181181
import "./tests/YARA.mjs";
182182
import "./tests/ParseCSR.mjs";
183183
import "./tests/XXTEA.mjs";
184+
import "./tests/COBS.mjs";
184185

185186
const testStatus = {
186187
allTestsPassing: true,

0 commit comments

Comments
 (0)