Add Base91 encoding/decoding operations

- Add complete Base91 implementation based on Joachim Henke's basE91 algorithm
- Includes both encoding (ToBase91) and decoding (FromBase91) operations
- Uses 91 printable ASCII characters for optimal space efficiency
- Better space efficiency than Base64 while maintaining readability
- Proper error handling for invalid characters during decoding
- Full JSDoc documentation

Files added:
- src/core/lib/Base91.mjs - Core Base91 implementation library
- src/core/operations/ToBase91.mjs - Base91 encoding operation
- src/core/operations/FromBase91.mjs - Base91 decoding operation
- Updated Categories.json to include Base91 operations

Author: Izai Alejandro Zalles Merino <zallesrene@gmail.com>
Technical details:
- Algorithm: Based on basE91 by Joachim Henke (http://base91.sourceforge.net/)
- Alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789%&()*+,./:;<=>?@[]^_`{|}~"
- Input types: ArrayBuffer for encoding, string for decoding
- Output types: string for encoding, ArrayBuffer for decoding
This commit is contained in:
Izai Alejandro Zalles Merino 2025-10-01 12:30:40 -04:00
parent 2a1294f1c0
commit aa4bb2b415
4 changed files with 205 additions and 0 deletions

View File

@ -33,6 +33,8 @@
"Show Base64 offsets", "Show Base64 offsets",
"To Base92", "To Base92",
"From Base92", "From Base92",
"To Base91",
"From Base91",
"To Base85", "To Base85",
"From Base85", "From Base85",
"To Base", "To Base",

125
src/core/lib/Base91.mjs Normal file
View File

@ -0,0 +1,125 @@
/**
* Base91 resources.
*
* Based on the original basE91 algorithm by Joachim Henke
* http://base91.sourceforge.net/
*
* @author CyberChef Base91 Implementation
* @copyright Crown Copyright 2024
* @license Apache-2.0
* @modified-by Izai Alejandro Zalles Merino <zallesrene@gmail.com> (ialejandrozalles)
* @modified-date 2025-10-01
* © 2025 Izai Alejandro Zalles Merino
*/
import OperationError from "../errors/OperationError.mjs";
/**
* Base91 alphabet - 91 printable ASCII characters
*/
const BASE91_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\"";
/**
* Decode table for Base91
*/
const BASE91_DECODE_TABLE = new Array(256).fill(-1);
for (let i = 0; i < BASE91_ALPHABET.length; i++) {
BASE91_DECODE_TABLE[BASE91_ALPHABET.charCodeAt(i)] = i;
}
/**
* Encode bytes to Base91
*
* @param {Uint8Array} data - Input byte array
* @returns {string} Base91 encoded string
* @modified-by Izai Alejandro Zalles Merino <zallesrene@gmail.com> (ialejandrozalles)
*/
export function encodeBase91(data) {
let accumulator = 0;
let accumulatorBits = 0;
let output = "";
for (let i = 0; i < data.length; i++) {
accumulator |= data[i] << accumulatorBits;
accumulatorBits += 8;
if (accumulatorBits > 13) {
let value = accumulator & 8191;
if (value > 88) {
accumulator >>= 13;
accumulatorBits -= 13;
} else {
value = accumulator & 16383;
accumulator >>= 14;
accumulatorBits -= 14;
}
output += BASE91_ALPHABET[value % 91] + BASE91_ALPHABET[Math.floor(value / 91)];
}
}
if (accumulatorBits > 0) {
output += BASE91_ALPHABET[accumulator % 91];
if (accumulatorBits > 7 || accumulator > 90) {
output += BASE91_ALPHABET[Math.floor(accumulator / 91)];
}
}
return output;
}
/**
* Decode Base91 string to bytes
*
* @param {string} str - Base91 encoded string
* @returns {Uint8Array} Decoded byte array
* @modified-by Izai Alejandro Zalles Merino <zallesrene@gmail.com> (ialejandrozalles)
*/
export function decodeBase91(str) {
let accumulator = 0;
let accumulatorBits = 0;
let value = -1;
const output = [];
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
const decodeValue = BASE91_DECODE_TABLE[charCode];
if (decodeValue === -1) {
throw new OperationError(`Invalid Base91 character: ${str[i]}`);
}
if (value === -1) {
value = decodeValue;
} else {
value += decodeValue * 91;
accumulator |= (value << accumulatorBits);
if (value > 88) {
accumulatorBits += 13;
} else {
accumulatorBits += 14;
}
value = -1;
while (accumulatorBits > 7) {
output.push(accumulator & 255);
accumulator >>= 8;
accumulatorBits -= 8;
}
}
}
if (value !== -1) {
accumulator |= value << accumulatorBits;
output.push(accumulator & 255);
}
return new Uint8Array(output);
}

View File

@ -0,0 +1,39 @@
/**
* @author Izai Alejandro Zalles Merino <zallesrene@gmail.com> (ialejandrozalles)
* @copyright © 2025 Izai Alejandro Zalles Merino
* @license Apache-2.0
*/
import { decodeBase91 } from "../lib/Base91.mjs";
import Operation from "../Operation.mjs";
/**
* From Base91 operation
*/
class FromBase91 extends Operation {
/**
* FromBase91 constructor
*/
constructor() {
super();
this.name = "From Base91";
this.module = "Default";
this.description = "Base91 is a binary-to-text encoding scheme that uses 91 printable ASCII characters. It provides better space efficiency than Base64 while maintaining readability. This operation decodes Base91-encoded text back to its original binary data.";
this.infoURL = "https://en.wikipedia.org/wiki/Binary-to-text_encoding#Encoding_standards";
this.inputType = "string";
this.outputType = "ArrayBuffer";
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const decoded = decodeBase91(input);
return decoded.buffer.slice(decoded.byteOffset, decoded.byteOffset + decoded.byteLength);
}
}
export default FromBase91;

View File

@ -0,0 +1,39 @@
/**
* @author Izai Alejandro Zalles Merino <zallesrene@gmail.com> (ialejandrozalles)
* @copyright © 2025 Izai Alejandro Zalles Merino
* @license Apache-2.0
*/
import { encodeBase91 } from "../lib/Base91.mjs";
import Operation from "../Operation.mjs";
/**
* To Base91 operation
*/
class ToBase91 extends Operation {
/**
* ToBase91 constructor
*/
constructor() {
super();
this.name = "To Base91";
this.module = "Default";
this.description = "Base91 is a binary-to-text encoding scheme that uses 91 printable ASCII characters. It provides better space efficiency than Base64 while maintaining readability. Base91 encodes arbitrary binary data using characters A-Z, a-z, 0-9, and various symbols (excluding hyphen, backslash, and single quote).";
this.infoURL = "https://en.wikipedia.org/wiki/Binary-to-text_encoding#Encoding_standards";
this.inputType = "ArrayBuffer";
this.outputType = "string";
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const data = new Uint8Array(input);
return encodeBase91(data);
}
}
export default ToBase91;