Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"From Bech32",
"To Base62",
"From Base62",
"To Base26",
"From Base26",
"To Base64",
"From Base64",
"Show Base64 offsets",
Expand Down
61 changes: 61 additions & 0 deletions src/core/operations/FromBase26.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @author NOVA-Openclaw
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";

/**
* From Base26 operation
*/
class FromBase26 extends Operation {

/**
* FromBase26 constructor
*/
constructor() {
super();

this.name = "From Base26";
this.module = "Default";
this.description = "Base26 is a notation for encoding arbitrary byte data using the uppercase letters A-Z. The input string is interpreted as a radix-26 number using A=0, B=1, ..., Z=25, and converted back to its big-endian byte representation.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
if (input.length < 1) return [];

const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const re = new RegExp("[^A-Za-z]", "g");
input = input.replace(re, "").toUpperCase();

if (input.length < 1) return [];

let number = 0n;
for (const c of input) {
number = number * 26n + BigInt(ALPHABET.indexOf(c));
}

if (number === 0n) return [0];

const bytes = [];
while (number > 0n) {
bytes.unshift(Number(number & 0xFFn));
number >>= 8n;
}

return bytes;
}

}

export default FromBase26;
58 changes: 58 additions & 0 deletions src/core/operations/ToBase26.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @author NOVA-Openclaw
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";

/**
* To Base26 operation
*/
class ToBase26 extends Operation {

/**
* ToBase26 constructor
*/
constructor() {
super();

this.name = "To Base26";
this.module = "Default";
this.description = "Base26 is a notation for encoding arbitrary byte data using the uppercase letters A-Z. The input bytes are interpreted as a big-endian unsigned integer, which is then represented in radix 26 using A=0, B=1, ..., Z=25.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}

/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
input = new Uint8Array(input);
if (input.length < 1) return "";

const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

let number = 0n;
for (const byte of input) {
number = (number << 8n) | BigInt(byte);
}

if (number === 0n) return ALPHABET[0];

let output = "";
while (number > 0n) {
output = ALPHABET[Number(number % 26n)] + output;
number /= 26n;
}

return output;
}

}

export default ToBase26;
149 changes: 149 additions & 0 deletions tests/operations/tests/Base26.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Base26 tests.
*
* @author NOVA-Openclaw
*
* @copyright Crown Copyright 2026
* @license Apache-2.0
*/

import TestRegister from "../../lib/TestRegister.mjs";

TestRegister.addTests([
{
name: "To Base26: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "To Base26: EAT -> JYGBS",
input: "EAT",
expectedOutput: "JYGBS",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "To Base26: Hello, World!",
input: "Hello, World!",
expectedOutput: "LBVLPVLUNRXHXRHSZIHQLL",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "To Base26: UTF-8",
input: "ნუ პანიკას",
expectedOutput: "HLPHPKXWXPTREJHEBQENHKRQAKLFKOWIWEXTDNAOPRQFRQLL",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "To Base26: zero byte",
input: "\x00",
expectedOutput: "A",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "To Base26: binary",
input: "\x01\x02\x03",
expectedOutput: "DTSL",
recipeConfig: [
{
op: "To Base26",
args: [],
},
],
},
{
name: "From Base26: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base26",
args: [],
},
],
},
{
name: "From Base26: JYGBS -> EAT",
input: "JYGBS",
expectedOutput: "EAT",
recipeConfig: [
{
op: "From Base26",
args: [],
},
],
},
{
name: "From Base26: lowercase and ignored characters",
input: "j y-g!b@s",
expectedOutput: "EAT",
recipeConfig: [
{
op: "From Base26",
args: [],
},
],
},
{
name: "From Base26: A -> zero byte",
input: "A",
expectedOutput: "\x00",
recipeConfig: [
{
op: "From Base26",
args: [],
},
],
},
{
name: "From Base26: DTSL -> binary",
input: "DTSL",
expectedOutput: "\x01\x02\x03",
recipeConfig: [
{
op: "From Base26",
args: [],
},
],
},
{
name: "To Base26 round-trip",
input: "The quick brown fox jumps over the lazy dog.",
expectedOutput: "The quick brown fox jumps over the lazy dog.",
recipeConfig: [
{
op: "To Base26",
args: [],
},
{
op: "From Base26",
args: [],
},
],
}
]);