Skip to content

Commit

Permalink
Merge pull request #1553 from sg5506844/base92
Browse files Browse the repository at this point in the history
Feature: Add Base92 operations
  • Loading branch information
a3957273 committed Feb 9, 2024
2 parents 35c2d43 + b118932 commit c13997b
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base92",
"From Base92",
"To Base85",
"From Base85",
"To Base",
Expand Down
44 changes: 44 additions & 0 deletions src/core/lib/Base92.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Base92 resources.
*
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import OperationError from "../errors/OperationError.mjs";

/**
* Base92 alphabet char
*
* @param {number} val
* @returns {number}
*/
export function base92Chr(val) {
if (val < 0 || val >= 91) {
throw new OperationError("Invalid value");
}
if (val === 0)
return "!".charCodeAt(0);
else if (val <= 61)
return "#".charCodeAt(0) + val - 1;
else
return "a".charCodeAt(0) + val - 62;
}

/**
* Base92 alphabet ord
*
* @param {string} val
* @returns {number}
*/
export function base92Ord(val) {
if (val === "!")
return 0;
else if ("#" <= val && val <= "_")
return val.charCodeAt(0) - "#".charCodeAt(0) + 1;
else if ("a" <= val && val <= "}")
return val.charCodeAt(0) - "a".charCodeAt(0) + 62;
throw new OperationError(`${val} is not a base92 character`);
}

55 changes: 55 additions & 0 deletions src/core/operations/FromBase92.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import { base92Ord } from "../lib/Base92.mjs";
import Operation from "../Operation.mjs";

/**
* From Base92 operation
*/
class FromBase92 extends Operation {
/**
* FromBase92 constructor
*/
constructor() {
super();

this.name = "From Base92";
this.module = "Default";
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const res = [];
let bitString = "";

for (let i = 0; i < input.length; i += 2) {
if (i + 1 !== input.length) {
const x = base92Ord(input[i]) * 91 + base92Ord(input[i + 1]);
bitString += x.toString(2).padStart(13, "0");
} else {
const x = base92Ord(input[i]);
bitString += x.toString(2).padStart(6, "0");
}
while (bitString.length >= 8) {
res.push(parseInt(bitString.slice(0, 8), 2));
bitString = bitString.slice(8);
}
}

return res;
}
}

export default FromBase92;
67 changes: 67 additions & 0 deletions src/core/operations/ToBase92.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import { base92Chr } from "../lib/Base92.mjs";
import Operation from "../Operation.mjs";

/**
* To Base92 operation
*/
class ToBase92 extends Operation {
/**
* ToBase92 constructor
*/
constructor() {
super();

this.name = "To Base92";
this.module = "Default";
this.description = "Base92 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const res = [];
let bitString = "";

while (input.length > 0) {
while (bitString.length < 13 && input.length > 0) {
bitString += input[0].charCodeAt(0).toString(2).padStart(8, "0");
input = input.slice(1);
}
if (bitString.length < 13)
break;
const i = parseInt(bitString.slice(0, 13), 2);
res.push(base92Chr(Math.floor(i / 91)));
res.push(base92Chr(i % 91));
bitString = bitString.slice(13);
}

if (bitString.length > 0) {
if (bitString.length < 7) {
bitString = bitString.padEnd(6, "0");
res.push(base92Chr(parseInt(bitString, 2)));
} else {
bitString = bitString.padEnd(13, "0");
const i = parseInt(bitString.slice(0, 13), 2);
res.push(base92Chr(Math.floor(i / 91)));
res.push(base92Chr(i % 91));
}
}

return res;

}
}

export default ToBase92;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import "./tests/Base58.mjs";
import "./tests/Base64.mjs";
import "./tests/Base62.mjs";
import "./tests/Base85.mjs";
import "./tests/Base92.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/ByteRepr.mjs";
import "./tests/CartesianProduct.mjs";
Expand Down
89 changes: 89 additions & 0 deletions tests/operations/tests/Base92.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Base92 tests.
*
* @author sg5506844 [sg5506844@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

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

TestRegister.addTests([
{
name: "To Base92: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 1",
input: "AB",
expectedOutput: "8y2",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 2",
input: "Hello!!",
expectedOutput: ";K_$aOTo&",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "To Base92: Spec encoding example 3",
input: "base-92",
expectedOutput: "DX2?V<Y(*",
recipeConfig: [
{
op: "To Base92",
args: [],
},
],
},
{
name: "From Base92: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
{
name: "From Base92: Spec decoding example 1",
input: "G'_DW[B",
expectedOutput: "ietf!",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
{
name: "From Base92: Invalid character",
input: "~",
expectedOutput: "~ is not a base92 character",
recipeConfig: [
{
op: "From Base92",
args: [],
},
],
},
]);

0 comments on commit c13997b

Please sign in to comment.